关于@Autowired后Spring无法注入的问题
在使用Spring框架开发的时候,难免有时会遇到@Autowired后无法自动主动,然后报空指针异常的错,下面说一下本人遇到的问题和解决办法.1.对于新手来说,最明显的不过是在applicationContext.xml文件上没有加<context:component-scan base-package="com.xxx"/>,或者明明写了,但Spring还是没有将该类注入到容器中..
在使用Spring框架开发的时候,难免有时会遇到@Autowired后无法自动主动,然后报空指针异常的错,下面说一下本人遇到的问题和解决办法.
1.对于新手来说,最明显的不过是在applicationContext.xml文件上没有加<context:component-scan base-package="com.xxx"/>,或者明明写了,但Spring还是没有将该类注入到容器中.其实,很多人都喜欢将包的路径写的非常的详细,比如:com.xxx.xxx.service等,这样做的好处是Spring启动的时候,扫描的类更少了,效率更高了.这样做其实无可厚非,但是有些时候,你难免会把所有需要交给Spring管理都放在这个包下,这就导致有些放在其它包下的,贴有@Controller,@Service,@Repository,@Component等注解的类也不会交给Spring管理了.所以,最稳妥的方法就是,扫描所有的包,如下
2.主要说一下本人最近遇到的一个比较常识性但又容易踩坑的地方,我们都知道,如果你想要通过@Autowired注入一个对象,那么前提是当前你所在的这个类本身需要被Spring管理, 举个例子: 你想要在一个Controller里注入一个Service,那么不仅这个Service要贴上@Service,这个Controller也要贴上@Controller, 如果当前的Controller并未贴上@Controller,那就无法注入,因为Spring并未管理当前Controller的生命周期.
重点来了,如果我的一个Service A明明已经贴上了注解,另一个Service B也贴上了注解,而且都被扫描到了,那为什么还是无法通过@Autowired注入呢?
原理其实很简单, 在SMSImpl里要调用receiptService的方法,首先SMSImpl这个类就要从容器中拿,而不能直接new出来,因为交给Spring管理的类一般默认是单例的,它会为这个对象注入属性,但自己new出来的类,就不会注入这个属性了,而我拿到的SMSImpl实现类其实就是我自己new出来的.
2.探讨Spring注解@Autowired属性注入、设值注入和构造注入的注入时机
相信大家在使用Spring进行开发的时候,肯定都使用过Spring的注解@Autowired来自动注入获取Bean。这里我们就简单的探讨一下一个小问题,一个之前被我忽略掉的问题:@Autowired属性注入,设值注入和构造注入的注入时机
提问阶段
@Autowired属性注入,设值注入和构造注入的注入 谁是最终生效者?他们之间的执行顺序是怎么样的?注入时机在什么阶段?
问题
我们都使用过@Autowired的三种注入方式
属性注入
设值注入
构造注入
但是你有没有想过如果我在同一个类中,对某个成员属性用三种方式分别注入,那么最后这个属性获取的Bean是哪种方式注入的呢?
又或者说,我们都知道有三种方式可以进行依赖注入,那么这三种方式在注入的时机有和不同呢?他们之间的注入顺序是怎么样的呢?
验证阶段
从提问阶段可以知道,我们总共需要解决两个问题:
首先验证一个猜想
在同一个类中,分别对同一个成员属性进行三种方式的依赖注入,最后的注入结果是哪种方式注入的?
同时要了解
属性注入、设值注入和构造注入的注入时机分别是什么?他们的执行顺序是怎么样的?
第一步: 首先我们定义一个接口和三个实现类
Hello接口
Hello1实现类
Hello2实现类
Hello3实现类
接口是用于做静态类型去实现多态的,每个实现类都有自己的名称name,用于最后确认接口最后被注入的Bean是哪个一个
第二步: 编写测试类HelloTest
/**
* 测试类
*/
测试类中实现的就是用三种方式都依赖注入Hello接口的实例,看看最后init(),use()方法中输出的hello对象是哪一个实现类的实例
属性注入对应Hello1
构造注入对应Hello2
设值注入对应Hello3
第三步: 编写Bean的后置处理器,从Bean的生命周期角度观察
MyInstantiationAwareBeanPostProcessor类
对Bean的生命周期不熟的可以先看这里,我这里就简单说明一下
InstantiationAwareBeanPostProcessor是一个Bean的后置处理器,我这里共实现了它的三个方法,三个方法都有自己的功能,我们可以把这个类简单理解成是Bean创建过程中的拦截器
postProcessBeforeInstantiation 在Bean要实例化前拦截到
postProcessAfterInstantiation 在Bean实例化后拦截
postProcessProperties则是在Bean填充属性阶段前拦截
总之,在我们这里的目的就是,分别输出Hello1,Hello2,Hello3三个Bean的实例化前,实例化后,实例化时,填充属性前的执行时刻,记录出他们的执行顺序是怎么样?
第四步: SpringBoot启动类启动
启动SpringBoot引导类,并在init方法中调用测试类的use方法
输出结果
结论阶段
结论一
我们可以看到HelloTest测试类的init方法和use方法输出的Hello实例都是Hello3;
所以从输出的结果中,我们可以得出结论:
- 成员属性经过属性注入,构造注入和设值注入之后,最终的结果是设值注入的Bean
结论二
属性注入,构造注入,设值注入的注入时机分析:
-
最终结论
所以最终的结论就是:如果三种注入方式都被使用了,最终生效的是设值注入; 他们之间的顺序是 构造注入 > 属性注入 > 设值注入
构造注入发生在Bean生命周期的实例化阶段,不是发生在填充属性阶段
属性注入和设值注入都是发生在Bean生命周期的填充属性阶段,且属性注入发生在设置注入之前
3. 注解注入和 xml注入谁先完成?
更多推荐
所有评论(0)