使用Lombok @AllArgsConstructor注解和@Value注解引发的问题和对Spring的一些思考

背景

昨天同事在导出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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!