Spring容器底层重点流程、源码跟踪技巧及Spring底层后置处理器的帮我们干的事儿

调试跟踪Srping运行流程则可以从

AnnotationConfigApplicationContext------>构造方法到AbstractApplicationContext 的refresh方法

大概创建Bean的流程

 

 

 

 

BeanPostProcessor原理:

可从容器类跟进顺序为:

AnnotationConfigApplicationContext-->refresh()-->

finishBeanFactoryInitialization(beanFactory)--->

beanFactory.preInstantiateSingletons()-->

760getBean(beanName)--->

199doGetBean(name, null, null, false)-->

317createBean(beanName, mbd, args)-->

501doCreateBean(beanName, mbdToUse, args)-->

541createBeanInstance(beanName, mbd, args)(完成bean创建)-->

578populateBean(beanName, mbd, instanceWrapper)(属性赋值)-->

579initializeBean(beanName, exposedObject, mbd)(Bean初始化)->

1069行到1710,后置处理器完成对init方法的前后处理.

 

最终得到如下如下

createBeanInstance(beanName, mbd, args)(完成bean创建)

populateBean(beanName, mbd, instanceWrapper); 给bean进行属性赋值

initializeBean() //初始化Bean方法内容如下,后置处理器对init方法的前后处理

{

  applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

  invokeInitMethods(beanName, wrappedBean, mbd) //执行自定义初始化

  applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)

}

从以上分析不难发现,bean的生命周期为bean的创建, 初始化, 当容器关闭时对单实例的bean进行销毁.

pring底层对BeanPostProcessor的使用

1,ApplicationContextAwareProcessor实现分析:

此类帮我们组建IOC容器,跟进ApplicationContextAwareProcessor我们发现, 这个后置处理器其实就是判断我们的bean有没有实现ApplicationContextAware 接口,并处理相应的逻辑,其实所有的后置处理器原理均如此.

那么怎么组建呢? 只需要实现 ApplicationContextAware 接口

步骤:

    1>, 新建Plane.java(Jeep.java复制一份即可)

         class Plane implements ApplicationContextAware

分析一下ApplicationContextAwareProcessor类的方法

a,在创建Plane对象,还没初始化之前, 先判断是不是实现了ApplicationContextAware接口,

如果是的话就调用invokeAwareInterfaces方法, 并给里面注入值;

b,进入invokeAwareInterfaces()方法,判断是哪个aware, 如果是ApplicationContextAware, 就将当前的bean转成ApplicationContextAware类型, 调用setApplicationContext(), 把IOC容器注入到Plane里去;

c,用debug调用; 测试用例打断点测试

d,也可看debug调用栈来分析;

: debug可以打在ApplicationContextAwareProcessor处理器类的applyBeanPostProcessorsBeforeInitialization()方法里, 方便调试, 当bean为Plane类型时,F5跟进看, 最终在 InvokeAwareInterfaces()方法里返回我们的IOC容器applicationContext.

 

2,BeanValidationPostProcess分析:数据校验,

   看BeanPostProcessor接口实现CTRL+T

 

了解即可,处理器的原理和其它处理器一致.

当对象创建完,给bean赋值后,在WEB用得特别多;把页面提交的值进行校验

 

 

 

3,InitDestroyAnnotationBeanPostProcessor

 

此处理器用来处理@PostConstruct, @PreDestroy, 怎么知道这两注解是前后开始调用的呢, 就是 InitDestroyAnnotationBeanPostProcessor这个处理的

以@PostConstruct为例, 为什么声明这个注解后就能找到初始化init方法呢?

 

      

 

 

总结: Spring底层对BeanPostProcessor的使用, 包括bean的赋值, 注入其它组件, 生命周期注解功能,@Async, 等等

比如@Autowire注解就是 AutowiredAnnotationBeanPostProcessor后置处理器来处理属性赋值的

Spring提供了5中自动装配模式

 @Autowired自动装配原理解析

自动装配:spring利用依赖注入(DI), 完成对IOC容器中的各个组件的依赖关系赋值

1,新建TestController.java   TestService.java  TestDao; 分别建在指定的包内,可看步骤2.

这些所有JAVA 类的对象扫描后都是保存在IOC容器中管理的;

          

 

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

 

3, 针对以上基础类建立完成后, 可以先做个测试

在TestService.java, 使用Autowired注入,并把testDao打印出来(在测试时方便对比)

 

4, 新建Cap9Test.java测试用例,比较TestService拿到testDao与直接从容器中拿到的testDao是否为同一个?

结果很明显是同一个testDao,地址一样

 

小结:

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

使用方式如下:

   TestService{

        @Autowired

        private TestDao testDao;//默认去容器中找id为”testDao”的bean

   }

 

 

5, 注意事项

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

   操作步骤:

Cap9MainConfig.java声明@Bean(“testDao2”)

 

并将TestDao加入flag属性和set, get及toString方法,用来分辨加载了哪个bean.

如何区分TestService是使用了(@Reponstry的testDao的flag=1)的bean还是(testDao2的flag=2)的bean?

测试步骤如下:

1,直接使用@Autowired, 将testDao注入到TestService

测试结果

 

2,如果一定要使用容器中的testDao2呢?操作如下:

测试结果

 

3,虽然以上定义了private TestDao testDao2, 但还是想加载bean id为testDao(flag=1)的bean,怎么办?此时可以使用@Autowired和@Qualifier结合来指定注入哪一个bean,

操作如下:

测试结果

 

4,如果容器中没有任何一个testDao, 会出现什么状况呢?

操作如下: 注释掉@Repository和@Bean("testDao2")

        

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

测试结果如下:

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

 

那怎么解决呢?

测试结果如下:

 

 

5,@Primary注解指定bean如何加载呢?

(:将以上原注释掉的@Repository和@Bean("testDao2") 恢复,见下图)

      

 

 

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

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

见下操作:  打开@Qualifier与@Primary注解.

 

 

测试结果:

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

 

 

那么@Primary的功能在哪呢?继续测试.....

测试结果:

 

 

 

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

将Qualifier和Autowired注释掉(注意: 此时@Primary 还没注释......)

 

测试结果:

效果也是一样的, 但它不先优先装配@Primary的bean

 

小结:@ResourceAutowired的区别如下:

@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

 

 

 

 

 

一,  @Autowired方法, 参数, 构造方法都可加载, 跟进源码看看

A>放到方法上的测试步骤:

    新建Moon.java

 

 

   新建Sun.java

setMoon()方法使用的参数,自定义类型的值从IOC容器中获取, 方法里的moon会从容器中拿到

 

以下是验证方式

在配置类要加入扫描bean包

测试:

  新建test02()测试方法

 

结果如下:(为同一个bean)

方法使用的参数,自定义类型的值从IOC容器中获取, 方法里的moon会从容器中拿到

 

 

  1. 将Autowired标记在有参构造器

构造函数的moon是从容器里拿到的

执行test02()测试;

 

 

 

 

 

同样,也可以放在构造器的参数位置也可以获取到IOC容器的bean.

 

结论: 不管@Autowired是放到参数, 方法还是构造方法, 都是从容器里取到的bean..

自动装配:Aware注入spring底层组件原理

    自定义组件想要使用Spring容器底层的组件(ApplicationContext, BeanFactory, ......)

    思路: 自定义组件实现xxxAware, 在创建对象的时候, 会调用接口规定的方法注入到相关组件:Aware

    之前讲过Plane.java就是使用这个

 

 

CTRL+SHIFT+T  找到Aware

查看有哪些接口继承了Aware接口

 

 

 

使用ApplicationContextAware接口为例, 实现接口

步骤:

1, 新建Light.java类, 实现ApplicaitonContextAware接口

 

 2,实现BeanNameAware接口

 

 

 

 

 2,实现EmbeddedValueResolverAware接口

 

3, 将Light类加入@Component, 声明为被扫描的组件

 

4,将test02()加入打印anno容器, 用来比如

 

5,测试, 执行test()02;

 

 

总结:把Spring底层的组件可以注入到自定义的bean中,ApplicationContextAware是利用ApplicationContextAwareProcessor来处理的, 其它XXXAware也类似, 都有相关的Processor来处理, 其实就是后置处理器来处理;

XXXAware---->功能使用了XXXProcessor来处理的, 这就是后置处理器的作用;

   ApplicaitonContextAware--->ApplicationContextProcessor后置处理器来处理的

 

问题: Spring怎么把applicationContext容器注入进来的呢????

    debug栈分析, 之前讲过的.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值