独特的角度来理解SpringBoot自动装配原理

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

前言

看了标题后,你应该能猜到我不是要从源码角度分析 SpringBoot 自动装配原理。再分析源码那也太没劲了,写文章很麻烦的,写一个很多人写过的文章更麻烦(但我依旧建议先了解原理,再找机会应用到项目中)。

我想记录的其实是:我在项目中无意间使用了SpringBoot的自动装配,做了一个 SpringBoot 自动装配的实战案例。

先说一下这个“无意间”的事件发生背景吧!作为一个后端程序员,对于接口入参的字段校验那是家常便饭,所以对校验非空的注解常用 @NotNull,字段为空会抛出 MethodArgumentNotValidException 异常,就是下面这一长串。

{
	"timestamp": "2021-07-02T02:12:29.868+0000",
	"status": 400,
	"error": "Bad Request",
	"errors": [
		{
			"codes": [
				"NotNull.loginDTO.password",
				"NotNull.password",
				"NotNull.java.lang.String",
				"NotNull"
			],
			"arguments": [
				{
					"codes": [
						"loginDTO.password",
						"password"
					],
					"arguments": null,
					"defaultMessage": "password",
					"code": "password"
				}
			],
			"defaultMessage": "密码不能为空",
			"objectName": "loginDTO",
			"field": "password",
			"rejectedValue": null,
			"bindingFailure": false,
			"code": "NotNull"
		}
	],
	"message": "Validation failed for object='loginDTO'. Error count: 1",
	"path": "/v1/test/login"
}
复制代码

但是我的固搭前端小伙伴只想要 @NotNull(message=“自定义异常”)里的 message 内容;所以我每次都会在项目中定义一个全局异常处理,一旦发生 MethodArgumentNotValidException 异常,就捕捉处理,如下面这样子的:

{
	"code": 400,
	"msg": "password:密码不能为空",
	"data": 0
}
复制代码

以前我每个项目都要写这个全局异常处理,烦得很!恰巧最近在搭建 maven 私服,我就想将这块功能写到我的 maven 私服中,以后只要在 pom 文件中依赖我的 maven,我都不用写了。

接下来就是我的实现步骤了,看我如何一步步“被逼”使用了 SpringBoot 的自动装配。

项目搭建发布

仓库的创建和 maven 项目的搭建在上一篇文章中已经说过,此处直接略过!想了解这部分内容可以查看「使用 Github 搭建 maven 私服

首先在 maven 项目中添加一个全局异常处理类 ValidParamExceptionHandler ,负责接口入参非空验证的异常处理。

@ControllerAdvice
public class ValidParamExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ApiResponse handlerMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder errorMessage = new StringBuilder(bindingResult.getFieldErrors().size() * 16);
        for (int i = 0; i < bindingResult.getFieldErrors().size(); i++) {
            if (i > 0) {
                errorMessage.append(",");
            }
            FieldError fieldError = bindingResult.getFieldErrors().get(i);
            errorMessage.append(fieldError.getField());
            errorMessage.append(":");
            errorMessage.append(fieldError.getDefaultMessage());
        }
        return new ApiResponse(400,errorMessage.toString(),0);
    }
}
复制代码

如果此时我直接打包发布并在新的项目中引入这个 maven 的依赖,你觉得新的项目中会做全局异常处理吗?我第一次就是原封不动的操作了一遍,结果就是不行!!!于是我在想,是因为 ValidParamExceptionHandler 类没有被 Spring 扫描到?那我加一个 @Component 注解不就可以了吗?但是当我打开 @ControllerAdvice 注解,我就知道我在想屁吃,因为 @ControllerAdvice 中已经包含了 @Component 注解。

Untitled.png

最后我上网搜索解决方式,看到了如下解决方案,即在 resources 文件夹下面的 META-INF 新建一个spring.factories 文件,并写入如下内容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.xinmachong.exception.ValidParamExceptionHandler
复制代码

第一行是固定的,第二行显然就是全局异常处理类的路径。

最后打包发布。

全局异常测试

新建 SpringBoot 项目并引入依赖,此处略过,想了解这部分内容可以查看「使用 Github 搭建 maven 私服」。

写一个测试接口如下图所示:

Untitled.png

其中 LoginDTO 是:

@Data
public class LoginDTO {
    @NotNull(message = "账号不能为空")
    private String account;
    @NotNull(message = "密码不能为空")
    private String password;
}
复制代码

测试接口的过程和结果如下图所示:

Untitled.png

入参只有一个 account,请求后看到返回信息为:“password:密码不能为空”。当时的内心就是两个字—握草;因为这个全局异常处理在新项目中竟然生效了。

总结

做的时候没有多想原理,但实现之后我肯定要弄明白的啊,所有人都知道了,关键点就在 spring.factories;但是为啥 spring.factories 这个文件这么屌?突然想起来之前看的 SpringBoot 自动装配实现的源码中好像有提到这个,通过查证,在源码的最后找到了 spring.factories。

一瞬间我突然理解了,原来这个就是通过 SpringBoot 自动装配实现了全局异常处理类的生效!豁然开朗。

我之前是有跟着博客上的好文章阅读过整个自动装配的源码的,也看懂了实现方式,但是说实话哈,看懂了和会使用了还差着一个实战项目呢。之间的差距自知。

最后留个小问题吧,

这个全局异常处理类肯定时时刻刻都在生效,绝对是 007 的工作方式,但是老板我年初一想给它放个假
咋办呢?作为老板的我,想让它工作它就得生效,想让它休息它就得没有存在感,咋搞?
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享