直接上手SpringBoot创建Web项目
SpringBoot声称可以很简单地创建独立的生产级的直接运行的Spring应用,那我们就来手撕一个试试。
创建项目打开IntelliJ IDEA,新建项目:Spring Initializr。
配置: Name: InitProj Language: Kotlin Type: Gradle Sdk: jbr-11 Java: 11 SpringBoot: 2.7.3 Dependences: Web>SpringWeb
start.spring.io 打不开?你可能需要一点上网技术。
配置阿里云仓库如果你想依赖包下载快一点,建议配置阿里云仓库。 打开 build.gradle.kts 在 repositories 中添加两具仓库:
maven {HelloWord
setUrl("https://maven.aliyun.com/repository/public/")
}
maven {
setUrl("https://maven.aliyun.com/repository/spring/")
}
创建Kolin类HelloController
@RestController
class HelloController {
@GetMapping("/hello")
fun hello(): String{
return "hello"
}
}
运行启动项目 浏览器输入 http://localhost:8080/hello Ok,小试水果刀,前菜结束。
响应数据结构化新需求:返回数据是这样 json
{"code":0,"message":"success","data":"hello"}
我们需要: 一个定义错误信息的 Data Class
data class AError(val code: Int, val msg: String)
一些常见的错误信息
inline val SUCCESS
get() = AError(0, "success")
inline val ERROR_UNKNOWN
get() = AError(-1, "unknown")
inline val PARAMS_NULL
get() = AError(-2, "缺少必填参数")
inline val SESSION_ERROR
get() = AError(-3, "session失效")
一个存储响应结果的 Data Class
data class Result(
var code: Int = 0,
var message: String = "",
var data: Any? = null
)
一些常见的结果
fun succeed(data: Any?) = Result(SUCCESS.code, SUCCESS.msg, data)
fun succeed() = succeed(null)
fun failed(err: AError) = Result(err.code, err.msg, null)
然后修改一下 hello 处理函数
@GetMapping("/hello")
fun hello(): Result{
return succeed("hello")
}
再次运行一下,结果是我们想要的。SpringBoot 帮我们做了比较多的处理,这里不作细论。
异常处理如果程序发生错误,前端会得到这样的结果:
{
"timestamp": "2022-08-26T14:34:48.440+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/hello"
}
我们希望得到这样的结果:
{"code":-1,"message":"unknown","data":null}
前端的格式比较统一,方便处理。 首先自定义一个异常
class MyException(val err: AError) : RuntimeException(err.msg)
然后添加一个异常处理组件,SpringBoot 会在异常发生时调用它来处理。
@ControllerAdvice
class ExceptionHandler {
companion object {
private const val TAG = "ExceptionHandler"
}
@ExceptionHandler(Exception::class)
@ResponseBody
fun handle(e: Exception): Result {
val result = if (e is MyException) {
failed(e.err)
} else {
failed(ERROR_UNKNOWN)
}
Log.e(TAG, e)
return result
}
}
这样如果发生我们定义的错误,会返回对应信息;其他错误则返回未知。 测试一下Hello:
@GetMapping("/hello")
fun hello(): Int{
// return 2/0
return failed(PARAMS_NULL)
}
结果是我们想要的。
参数校验请求参数需要先过滤一遍,防止引入未知错误。 虽然 Spring 有@Valid 注解可以很方便地验证请求参数的合法性,但是我更倾向于自定义验证内容。 首先定义一个抽象类 ARequest,作为所有请求参数的父类。
abstract class ARequest {
// 验证参数
abstract fun validate()
// 检验参数是否为空
fun paramsNotBlank(vararg params: String){
params.forEach {
if(it.isBlank()){
throw MyException(PARAMS_NULL)
}
}
}
}
然后定义一个切面,在特定函数执行前进行参数校验。
@Aspect
@Component
class RequestAspect {
companion object{
private const val TAG = "RequestAspect"
}
@Pointcut("execution(public * *(ARequest+,..))")
fun point(){}
@Before("point()")
fun checkRequest(join: JoinPoint){
join.args.forEach {
if(it is ARequest){
it.validate()
}
}
}
}
面向切面编程需要引入依赖 implementation("org.springframework.boot:spring-boot-starter-aop:2.7.3")
切点"execution(public * *(ARequest+,..))"的意思是:在任何类型为public,第一个参数是ARequest的子类的函数处作切点。
本例中如果需要指定打招呼的人名,可以这样定义请求参数:
data class HelloRequest(
var name: String = "",
): ARequest(){
override fun validate() {
paramsNotBlank(name)
}
}
修改 Hello 函数:
@GetMapping("/hello")
fun hello(request: HelloRequest): Result{
return succeed("Hello ${request.name}")
}
不指定name参数时,返回:
{"code":-2,"message":"缺少必填参数","data":null}
指定name 为 Tom , 返回:
{"code":0,"message":"success","data":"Hello Tom"}总结
SpringBoot 为我们做了相当多的配置工作,使用起来很便捷。本篇文章没有原理解释,更像一道练习题,用来熟悉 SpringBoot 的起手式,更多细节以后探讨。
源码:https://gitee.com/yoshii_x/init-proj.git
版权声明
本文仅代表作者观点,不代表博信信息网立场。