Spring源码解读之BeanFactoryPostProcessor的处理
前言
前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得。我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPostProcessor是如何发挥作用的,当时觉得讲的还行,但是现在看来,太粗劣了,很多地方没涉及到,而且重点都被我忽略了,简直就是蠢得不行。现在就用这篇文章弥补一下前文中对BeanFactoryPostProcessor的讲解,争取把重点讲到,至于BeanPostProcessor,由于涉及到的东西太多,限于本人目前的水平只能作罢,待后面感悟成熟了再来补充。
我们以AnnotationConfigApplicationContext为例来构建测试类,先附上此次打断点调试的三个简约到极致的测试类:
public class SpringTest {
public static void main(String[] args) {
// 从这两行代码,实地跟踪考察Spring中的流程
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
applicationContext.getBean(Teacher.class).teach();
}
}
package myPackage;
import org.springframework.stereotype.Service; @Service
public class Teacher {
public Teacher () {
System.out.println("Teacher init");
}
public void teach () {
System.out.println("teach");
}
}
package myPackage;
import org.springframework.context.annotation.ComponentScan; @ComponentScan("myPackage")
public class ScanConfig {
}
1、洞悉启动容器时的准备工作
熟悉一些Spring的道友应该都知道,refresh方法中的invokeBeanFactoryPostProcessors方法实现了对BeanFactoryPostProcessor实现类的处理。大家如果只看invokeBeanFactoryPostProcessors方法的话,不会发现有何异常之处,此方法虽然较长,但是处理逻辑很清晰,先对重写了BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor方法的实现类进行处理,后对重写了BeanFactoryPostProcessor的方法的实现类做了处理。但是如果心细的话,你会发现问题,Spring是如何将@ComponentScan("myPackage")注解发挥作用的?这时带着这样的问题,我们再回过头来看容器的构造方法,就会在这平实的表面下发现意想不到的 "杀机"。
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
通过这个构造方法可以知道,在第二行将我们的测试类ScanConfig 注册进了容器中,但这只是注册,注册之后是如何调用如何实现了@ComponentScan("myPackage")这个注解的包扫描的呢?这时我们将目光锁定this()方法。点进去后发现是这样的:
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
在第二行新建reader对象时,调用了这个构造方法:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
其中的第六行,最终调用了AnnotationConfigUtils#registerAnnotationConfigProcessors方法,而就是在这个方法中完成了对多个重要Bean的注册,跟我们关系比较大的有以下几个:
// BeanDefinitionHolder只是存放BD的,里面有三个属性:BD对象、beanName以及别名组成的String[]
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
// 注册最关键的类,对应的类为ConfigurationClassPostProcessor,父类的父类是BeanFactoryPostProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
// 将BD注入进容器中,没经过什么处理,只是放入了DefaultListableBeanFactory中的beanDefinitionMap跟存放beanName的list中
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 此类实现了BeanPostProcessor,用于处理@Autowired、@Value注解
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 此类也实现了BeanPostProcessor,用于处理有@Required注解的方法
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
其中第一个对应的类就是我们重点关注的对象 ConfigurationClassPostProcessor类,查看此类的组成,发现它实现了BeanDefinitionRegistryPostProcessor接口,而此接口正是BeanFactoryPostProcessor的子接口。此时,萦绕在我们心头的迷雾开始渐渐散开,我们仿佛能抓到一闪而过的逻辑走向,现在让我们带着之前的发现,进入正主invokeBeanFactoryPostProcessors方法中一探究竟。
2、invokeBeanFactoryPostProcessors
该方法位于AbstractApplicationContext的refresh方法中,如下所示:
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 // Prepare this context for refreshing.
4 prepareRefresh();
5
6 // Tell the subclass to refresh the internal bean factory.
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8
9 // Prepare the bean factory for use in this context.
10 prepareBeanFactory(beanFactory);
11
12 try {
13 // Allows post-processing of the bean factory in context subclasses.
14 postProcessBeanFactory(beanFactory);
15
16 // Invoke factory processors registered as beans in the context.
17 invokeBeanFactoryPostProcessors(beanFactory);
即第17行调用的方法。初学者看到这个方法的内部实现时,会发现此方法无外乎是找到所有实现了BeanDefinitionRegistryPostProcessor跟BeanFactoryPostProcessor接口的类,按照优先级(实现了PriorityOrdered接口的先于实现了Ordered接口,前两者均先于未实现的,且同一类中按照getOrder方法返回值排优先级)顺序执行它们的重写方法,先执行BeanDefinitionRegistryPostProcessor的重写方法,再执行BeanFactoryPostProcessor的重写方法,很容易理解的逻辑。但是现在我们是带着前面准备工作中得到的线索来的,此时再看,就能透过这个方法朴实的外表发现它潜藏的凶险,它如平地一声雷般让人猛地惊出一身冷汗。
我们打断点进入PostProcessorRegistrationDelegate类中的下面方法中
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {}
先略过开始对手动添加进去的beanFactoryPostProcessors处理逻辑,看后面的部分代码(由于此方法代码较多,此处就不全部粘贴出来了,因为逻辑很好理解,所以只粘贴重点):
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
// 1.2 先处理实现了PriorityOrdered的类
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 此处通过getBean来生成ConfigurationClassPostProcessor实例对象
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 就一个对象,有啥好排序的
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
registryPostProcessors.addAll(priorityOrderedPostProcessors);
// 执行ConfigurationClassPostProcessor中的重写方法postProcessBeanDefinitionRegistry,会将所有加了注解的类注册到容器中
// 此处才是整个invokeBeanFactoryPostProcessors方法的核心所在,需要详述 下面进入ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry中一探究竟
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
debug到第一行的时候,会发现此处获取到的postProcessorNames 中只有一个值,就是前面准备工作中通过硬编码往容器里注册的ConfigurationClassPostProcessor类。下面的逻辑就是进行各种判断,最后在第19行完成了对ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry方法的调用。
就是在这个后置处理方法中,完成了@ComponentScan("myPackage")中对包的扫描,完成了所有Bean的注册。执行完这个方法后,你会发现beanDefinitionMap中所有应该容器管理的类全都齐活了,包括其他的后置处理器。这样,后面继续调用beanFactory.getBeanNamesForType方法时,获取到的是所有满足条件的类,后面的工作就会有条不紊的开展下去了。
总结
本文着重追溯了BeanFactoryPostProcessor及其子接口是如何在Spring中发挥作用的。先通过Spring初始化容器时注册进去的ConfigurationClassPostProcessor类触发对其他类的扫描,待全部注册进容器后,再从容器中取对应的BeanFactoryPostProcessor及其子接口的实现类,逐一对重写方法进行调用。
虽然魔鬼在细节,但这也正是解读源码的快乐之处,不是吗?
Spring源码解读之BeanFactoryPostProcessor的处理的更多相关文章
- Spring源码分析之`BeanFactoryPostProcessor`调用过程
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...
- Spring源码解析之BeanFactoryPostProcessor(三)
在上一章中笔者介绍了refresh()的<1>处是如何获取beanFactory对象,下面我们要来学习refresh()方法的<2>处是如何调用invokeBeanFactor ...
- Spring源码解读--(一)源码下载
走在Java程序员这条路上,网上Java各种工具满天飞,写个简单的CRUD,相信是个开发都能写出来,于是在思考如何可以在同行业中更有竞争力(其实就是如何赚更多钱).那么,老大给我推荐了Spring源码 ...
- 【Spring源码解读】bean标签中的属性
说明 今天在阅读Spring源码的时候,发现在加载xml中的bean时,解析了很多标签,其中有常用的如:scope.autowire.lazy-init.init-method.destroy-met ...
- Spring源码解读:核心类DefaultListableBeanFactory的继承体系
1 简介 我们常用的ClassPathXmlApplicationContext是AbstractRefreshableApplicationContext的子类,而DefaultListableBe ...
- Spring源码解读Spring IOC原理
一.什么是Ioc/DI? IoC 容器:最主要是完成了完成对象的创建和依赖的管理注入等等. 先从我们自己设计这样一个视角来考虑: 所谓控制反转,就是把原先我们代码里面需要实现的对象创建.依赖的代码,反 ...
- spring源码解读之 JdbcTemplate源码
原文:https://blog.csdn.net/songjinbin/article/details/19857567 在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数 ...
- 《spring源码解读》 - IoC 之解析 import 标签
在上一文中我们分析了注册 BeanDefinition 的过程,在其中我们了解到在解析跟节点和子节点时分两种情况,对于默认名称空间的标签我们通过 DefaultBeanDefinitionDocume ...
- Spring源码解析之BeanFactoryPostProcessor(一)
BeanFactoryPostProcessor 在前面几个章节,笔者有介绍过BeanFactoryPostProcessor,在spring在解析BeanDefinition之后,正式初始化bean ...
随机推荐
- prototype __proto__ Function
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象.(注意:是函数才有prototype属性) 而__proto__属性每一个对象都有. 在js中如果A对象是由B函数构 ...
- 关于ExpandableListView的一个小例子
喜欢显示好友QQ那样的列表,可以展开,可以收起,在android中,以往用的比较多的是listview,虽然可以实现列表的展示,但在某些情况下,我们还是希望用到可以分组并实现收缩的列表,那就要用到an ...
- DDD实战8_2 利用Unity依赖注入,实现接口对应实现类的可配置
1.在Util类库下新建DIService类 /// <summary> /// 创建一个类,对应在配置文件中配置的DIServices里面的对象的 key /// </summar ...
- 【Java】Java中的数据类型
Java 语言是一种强类型语言.通俗点说就是,在 Java 中存储的数据都是有类型的,而且必须在编译时就确定其类型.Java 数据类型分为两类,如图: 在 Java 的领域里,基本数据类型变量存的是数 ...
- WPF 播放音频使用的SoundPlayer和MediaPlayer
WPF中,最简单最容易播放音频的方式是使用SoundPlayer类.它是.NET Framework 2.0的一部分,是对Win32 PlaySound API的封装. 它具有以下限制: 1)仅支持. ...
- 图像滤镜艺术---保留细节的磨皮滤镜之PS实现
原文:图像滤镜艺术---保留细节的磨皮滤镜之PS实现 目前,对于人物照片磨皮滤镜,相信大家没用过也听过吧,这个滤镜的实现方法是多种多样,有难有简,有好有差,本人经过长时间的总结,得出了一种最简单,效果 ...
- SQL Server 2016新特性:DROP IF EXISTS
原文:SQL Server 2016新特性:DROP IF EXISTS 在我们写T-SQL要删除某个对象(表.存储过程等)时,一般会习惯先用IF语句判断该对象是否存在,然后DROP,比如: 旧 ...
- Cindy components(配色很不错)
https://sourceforge.net/projects/tcycomponents/
- 【转】跟面试官聊.NET垃圾收集,直刺面试官G点
装逼的面试官和装逼的程序员 我面试别人的时候,经常是按这种路子来面试: 看简历和面试题,从简历和面试题上找到一些技术点,然后跟应聘者聊. 聊某个技术点的时候,应聘者的回答会牵涉到其他的技术点,然后我会 ...
- C#关于多线程的笔记
Thread thNetwork; thNetwork = new Thread(new ThreadStart(GetNetworkInfo));//创建一个线程 thNetwork.Start() ...