关于java:Spring第四天BeanPostProcessor源码分析彻底搞懂IOC注入及注解优先级问题

39次阅读

共计 4442 个字符,预计需要花费 12 分钟才能阅读完成。

点击下方原文链接查看:

不要再说不会 Spring 了!Spring 第一天,学会进大厂!

Spring 第二天,你必须晓得容器注册组件的几种形式!学废它吊打面试官!

Spring 第三天,详解 Bean 的生命周期,学会后让面试官无话可说!

明天解说 Spring 底层对 BeanPostProcessor 的应用。

背景:BeanPostProcessor 是什么?有什么用?

Spring 所有对 Bean 的解决,BeanPostProcessor 接口都贯通其中,都离不开该接口。BeanPostProcessor 接口能够治理 bean 工厂内所有 beandefinition(未实例化)数据,可任意批改属性。

首先来看一下有哪些类实现了 BeanPostProcessor 接口

能够从上图看出,有这么多的接口及类都实现了 BeanPostProcessor 接口,明天重点来解说 ApplicationContextAwareProcessorBeanValidationPostProcessor 类 InitDestoryAnnotationBeanPostProcessor 类 的原理。其中 ApplicationContextAwareProcessor 类 是为一些 Aware 类型的接口,注入对应的属性值。此类帮咱们组件 IOC 容器,是一个后置处理器,跟进 ApplicationContextAwareProcessor 类咱们发现,这个后置处理器其实就是判断咱们的 bean 有没有实现 ApplicationContextArea 接口,并解决相应的逻辑,所有的后置处理器的原理都是如此。

(其余的类和接口在这里暂且不探讨,重点关注 ApplicationContextAwareProcessor 类是如何帮忙组件 IOC 容器的。ps:单词都很长,肯定要留神辨别对应的类和接口)

一、ApplicationContextAwareProcessor 实现剖析

问题:ApplicationContextAwareProcessor 如何帮忙咱们组件 IOC 容器呢?

答复:只须要实现 ApplicationContextAware 接口即可。

定义实体类 Plane.java 类

咱们再来剖析一下 ApplicationContextAwareProcessor 类的源码办法

1. 在创立 Plane 对象,并且还没有初始化之前,先判断是否是实现了 ApplicationContextAware 接口,如果实现了的话,就调用 invokeAwareInterfaces()办法,并给外面注入值。

  1. 接下来咱们进入 invokeAreaInterfaces()办法,判断是哪一个 aware,如果是 ApplicationContextAware,就将以后的 bean 转成 ApplicationContextAware 类型,调用 setApplicationContext(),把 plane 注入到 IOC 容器中去。

3. 通过 debug 调用,测试用例打断点来调试:

  1. 也能够用 debug 调用栈来剖析。

ps:debug 能够打在 ApplicationContextAwareProcessor 处理器类的 PostProcessorsBeforeInitialization()办法里,便于调试,当 bean 为 plane 类型时,F5 跟进看,最终在 InvokeAwareInterfaces()办法里返回咱们的 IOC 容器 applicationContext。

二、BeanValidationPostProcess 实现剖析

该类次要用于数据校验,当创立完对象,给 bean 赋值后,在 Web 用的特地多;把页面提交的值进行校验。

通过源码能够从 BeanValidationPostProcess 类中能够看到 87 和 95 行,有 postProcessBeforeInitialization() 和 postProcessAfterInitialization() 办法,这两个办法别离在 bean 初始化之前 和 初始化之后进行校验。

三、InitDestoryAnnotationBeanPostProcessor 实现剖析

此类用来解决 JSR250(JDK 提供)标准中 @PostConstruct 和 @PreDestory,怎么晓得这两注解是前后开始调用的呢?

定义实体类 Jeep.java 类

以 @PostConstruct 为例,为什么申明了这个注解之后,就能够找到初始化 init 办法呢?

继续执行,执行到 invokeInitMethods()办法中能够看到,利用反射调用了该办法

从下面的 debug 调试能够得悉,InitDestoryAnnotationBeanPostProcessor 来解决了这两个注解是前后开始调用的。

接下来,咱们来理解一下 @Autowired 主动拆卸

四、@Autowired 主动拆卸

什么是主动拆卸?主动拆卸:Spring 利用依赖注入(DI),实现对 IOC 容器中的各个组件的依赖关系赋值。

  1. 新建 TestController.java、TestService.java、TestDao.java 类别离建设在指定包内,通过配置类,将所有这些 Java 对象扫描后保留在 IOC 容器中治理。

2. 新建配置类,扫描并将以上 bean 都扫描并加载到容器中

  1. 针对以上类建设实现后,在 TestService.java 中,应用 @Autowired 注入,并把 testDao 打印进去

4. 新建测试类,比拟 TestService 拿到 testDao 与间接从容器拿到的 testDao 是否为同一个?

后果很显著,打印进去是同一个 testDao,地址一样

小结:

@Autowired 示意默认优先按类型去容器中找对应的组件,相当于 anno.getBean(TestDao.class)去容器中获取 id 为 testDao 的 bean,并注入到 TestService 的 bean 中;

应用形式如下:

public class TestService {// 默认去容器中找 testDao 的 bean` `@Autowired` private TestDao testDao;}

5. 注意事项

5.1 如果容器中找到多个 testDao,会加载哪一个 testDao 呢?

操作步骤

在配置类申明 @Bean(“testDao2”)

并将 TestDao 退出 flag 属性和 get、set 以及 toString()办法,用来分辨加载了哪个 bean。

如何辨别 TestService 是应用了 (@Reponstry 的 testDao 的 flag=1) 的 bean 还是 (testDao2 的 flag=2) 的 bean?

测试步骤如下:

  1. 间接应用 @Autowired, 将 testDao 注入到 TestService

bean id 为 testDao,依据 id 默认去找 @Reponsitory 注解的 testDao

测试后果为:

service…testDao…TestDao [flag=1]

2. 如果肯定要应用容器中的 testDao2 呢? 操作如下:

将 bean id 指向 testDao2 即可

测试后果为:

service…testDao…TestDao [flag=2]

  1. 尽管以上定义了 private TestDao testDao2, 但还是想加载 bean id 为 testDao(flag=1)的 bean, 怎么办? 此时能够应用 @Autowired 和 @Qualifier 联合来指定注入哪一个 bean,

操作如下:

通过应用 @Qualifier 注解,指定加载 testDao

测试后果:

service…testDao…TestDao [flag=1]

  1. 如果容器中没有任何一个 testDao, 会呈现什么情况呢?

操作如下: 正文掉 @Repository 和 @Bean(“testDao2”)

此时容器启动时这两个 bean 都不会加载(因为注解被正文啦 …….)

测试后果如下:

很显著报错了, 因为 @Autowired 注解里的属性默认 required=true. 必须找到 bean

那怎么解决呢?

将 TestService 中 @Autowired 中 required 设置为 false,指定为非必须,当容器中无此 bean,也不会报错。

测试后果如下:

null

  1. @Primary 注解指定 bean 如何加载呢?

(注: 将以上原正文掉的 @Repository 和 @Bean(“testDao2”) 复原, 见下图)

重要: 为了验证 @Qualifier 与 @Primary 两注解的加载程序, 测试如下:

当对于 testDao 在容器中同时存在多个时, 且 @Qualifier 与 @Primary 注解同时存在, 会产生什么呢?

见下操作:  关上 @Qualifier 与 @Primary 注解.

测试后果:

TestDao [flag=1]

unit….test….TestDao [flag=2]

此时只能阐明一点: @Qualifier 是依据 bean id 指定获取 testDao, 不受 @Primary 影响

那么 @Primary 的性能在哪呢? 持续测试 …..

测试后果:

5.2 除了 @Autowired, 是不是还用过 @Resource(JSR250)  和 @Inject(JSR330)

将 Qualifier 和 Autowired 正文掉(留神: 此时 @Primary 还没正文 ……)

测试后果:

成果也是一样的, 但它不先优先拆卸 @Primary 的 bean

小结:@Resource 和 Autowired 的区别如下:

@Resource 和 Autowired 一样能够拆卸 bean

@Resource 毛病: 

① 不能反对 @Primary 性能

② 不能反对 @Autowired(required = false)的性能

当然也能够在 TestService 里按以下形式指定要注入的 Bean

测试后果:

5.3 @Inject 主动拆卸的应用:

注:@Inject 与 @Autowired 的区别如下:

@Inject 和 Autowired 一样能够拆卸 bean, 并反对 @Primary 性能, 可用于非 spring 框架.

@Inject 毛病: 但不能反对 @Autowired(required = false)的性能, 须要引入第三方包 javax.inject

操作步骤:

1,pom.xml 导入 javax.inject 包

2, 应用 @Inject 注解

论断:@Inject 不反对 required=false,  但反对 primary

Autowired 属于 spring 的, 不能脱离 spring,  @Resource 和 @Inject 都是 JAVA 标准

举荐大家应用 @Autowired

这一章节​比拟生涩难懂。能够在下方留言进行一起沟通探讨。

(来自享学 IT 的​总结)

浏览原文:Spring 第四天,BeanPostProcessor 源码剖析,彻底搞懂 IOC 注入及注解优先级问题!

今天将会介绍一章重头戏,AOP 底层介绍,实践实际​。

关注公众号【Java 极客思维】

正文完
 0