背景
昨天同事在导出excel的时候,有一列为空,于是问我怎么回事,我看了看,就是数据库这个字段没有值,想了想,可能当时设计有问题,所以这一列的值一直没有设值,这个值是固定的,不会变的,所以数据库用一个字段来存一个固定的值,其实是设计有问题的,假设存了十万行数据,那么这个值也是不变的,毫无意义,还浪费资源,所以我就把这个固定的值放到SpringBoot的yml文件中,然后在导出数据的时候,直接从yml获取这个值填充进去就行了,我暂时就直接使用@Value注解获取这个值,但是启动却报错了,看着代码貌似没什么问题(可能是我菜吧)。
伪代码
下面就是伪代码,不难看出,下面使用了Lombok的@AllArgsConstructor,所以Spring会使用构造函数来装配Bean。
@RestController
@AllArgsConstructor
public class TestController {
@Value("${appInfo.name}")
private String appName;
private TestService testService;
@GetMapping("/exportExcel")
public void exportExcel(@Valid ServiceEvaluateVO serviceEvaluateVO, HttpServletResponse response) {
//省略若干代码
}
}
上面我想通过@Value注解来获取yml的配置信息,但是项目启动报错如下;
Parameter 0 of constructor in io.steakliu.spring.boot.controller.TestController required a bean of type 'java.lang.String' that could not be found.
可以看出Spring找不到一个类型为’java.lang.String’的bean,其实就是找不到appName,因为使用@AllArgsConstructor,所以所有属性在spring进行构造函数实例化的时候都会进行装配,故找不到appName。
解决方案
这个问题很好解决,在解决这个问题之前,我们要清楚@Value是发生在那个阶段,@Value是发生在属性填充阶段,而上面appName被加入构造函数中,且使用的是Lombok的@AllArgsConstructor,所以这个阶段一定找不到appName。
手动创建建构函数
如果不使用@AllArgsConstructor,而是自己创建构造函数,那么可以稍微做一点改动,就能对appName进行赋值,如下:
@RestController
public class TestController {
private String appName;
private TestService testService;
public TestController(@Value("${app.name}") String appName, TestService testService){
this.appName = appName;
this.testService = testService;
}
@GetMapping("/exportExcel")
public void exportExcel(@Valid ServiceEvaluateVO serviceEvaluateVO, HttpServletResponse response) {
//省略若干代码
}
}
上面手动创建构造函数,然后在appName前面加上@Value注解,那么appName就能进行属性填充,不过,这样有一个问题,那就是需要手动创建构造函数,如果要注入的bean很多,构造函数就会变得很庞大,所以使用@AllArgsConstructor就能让代码变得清洁,但是遇到我这种傻逼非要在这里使用@Value,那么就不好办了。
单独写入一个类
当然,我们也可以直接将它写入一个类中,这样直接在别处注入就可以,扩展性也好。
@Component
@Data
public class AppInfo {
@Value("${app.name}")
private String appName;
}
如下,直接注入AppInfo这个bean,然后就不用手动去创建构造函数,可以安心使用@AllArgsConstructor。
@RestController
@AllArgsConstructor
public class TestController {
private AppInfo appInfo;
private TestService testService;
@GetMapping("/exportExcel")
public void exportExcel(@Valid ServiceEvaluateVO serviceEvaluateVO, HttpServletResponse response) {
//省略若干代码
}
}
不使用构造函数注入
当然,也可以不使用构造函数注入bean,可以使用@Autowired,@Resource方式进行注入bean,这样也是没问题的,这样所有的属性都会在属性填充阶段进行赋值,就不存在找不到属性了。
```java
@RestController
public class TestController {
@Value("${app.name}")
private String appName;
@Autowired
private TestService testService;
@GetMapping("/exportExcel")
public void exportExcel(@Valid ServiceEvaluateVO serviceEvaluateVO, HttpServletResponse response) {
//省略若干代码
}
}
总结
其实对于这个问题,是很简单的,也很容易排查出来,这样的错误也是不应该出现的,其实归根结底,还是没有去关注到@AllArgsConstructor注解,不过这样的问题在编译阶段都过不了,所以不存在线上出错。
这个问题主要考察的是Spring生命周期的一些知识,从中能引申出很多的知识点,@Value是发生在属性填充阶段,而使用构造函数进行属性的装配是发生在实例化阶段,两个不在一个阶段,所以找不到就报错了。
“
今天的分享就到这里,感谢你的观看,我们下期见。
”
原文始发于微信公众号(刘牌):使用Lombok @AllArgsConstructor注解和@Value注解引发的问题和对Spring的一些思考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/150634.html