今天写业务的时候突然遇到一个Spring组件注入为null的问题,其实早在之前有一次就遇到过了,一直没处理,今天终于发现问题记录一下。
原先我只是为了使用建造者模式构造一个对象并调用目标方法,具体情况如下
其中的http()为静态方法,用于创建SimpleTransfer实例。如下所示
一开始并没有问题,但是后面为了封装服务调用接口及相关的参数,我又需要引入一个组件,以ConnectionBaseUtil为例
先看一下ConnectionBaseUtil
这里的ConnectionBaseUtil注册为Spring组件,同时注入了layoutUtil和restTemplate两个Spring实例,这一块是没有问题的
但是回到SimpleTransfer,我原先的注入情况如下
@Slf4j
@Data
@Component
public class SimpleTransfer extends AbstractRequestWay{
@Resource
private ConnectionBaseUtil connectionBaseUtil;
.
.
.
}
看上去没有毛病,正常用法嘛,但其实这里忽略了一个点,那就是我们在上面使用SimpleTransfer的建造模式的时候,首先是调用的是SimpleTransfer的无参构造器创建的类实例,而我们的Spring容器中本身就存在其中两个bean,分别是SimpleTransfer和ConnectBaseUtil,同时要知道,默认情况下我们在没有指定模式时是单例模式。
也就是说,如果你这个时候new一个SimpleTransfer,此SimpleTransfer非彼SimpleTransfer,是完全不同的两个对象。后者不是通过Spring容器创建的自然就不会使用@Resource或@Autowired注入ConnectBaseUtil组件
那么这两者有什么区别呢?其实区别不大,Spring容器创建实例后(以单例模式讲),整个生命周期都由Spring管理。那么问题出在哪里。其实不管是@Resource还是@Autowired都一样,这两者都是在创建Spring组件时识别并处理的,而普通new一个实例是没有处理这些注解的,是无法注入的,其实问题基本已经解决了。
我们在上面的静态方法http()创建SimpleTransfer实例时,可以找到ConnectBaseUtil,但是并没有赋值,那么就会得到null
如何解决呢?
有两种方式:
1、以静态变量的方式将地址指向我们的组件
这样我们就可以在Spring创建SimpleTransfer组件时,将ConnectionBaseUtil注入进去并与静态成员变量绑定,即使我们使用无参构造器创建实例也无需担心使用不到我们注册的组件了。
注意这里的setConnectionBaseUtil不能为statis
2、不使用无参构造的方式自行创建实例
这种方式比较简单,直接使用注入的方式获取实例并调用方法,注意这里就不需要使用http()方法了。