Spring源码解读之BeanFactoryPostProcessor的处理

前言

前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得。我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPostProcessor是如何发挥作用的,当时觉得讲的还行,但是现在看来,太粗劣了,很多地方没涉及到,而且重点都被我忽略了,简直就是蠢得不行。现在就用这篇文章弥补一下前文中对BeanFactoryPostProcessor的讲解,争取把重点讲到,至于BeanPostProcessor,由于涉及到的东西太多,限于本人目前的水平只能作罢,待后面感悟成熟了再来补充。

我们以AnnotationConfigApplicationContext为例来构建测试类,先附上此次打断点调试的三个简约到极致的测试类:

1 public class SpringTest {
2
3     public static void main(String[] args) {
4         // 从这两行代码,实地跟踪考察Spring中的流程
5         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
6         applicationContext.getBean(Teacher.class).teach();
7     }
8 }
 1 package myPackage;
 2 import org.springframework.stereotype.Service;
 3
 4 @Service
 5 public class Teacher {
 6     public Teacher () {
 7         System.out.println("Teacher init");
 8     }
 9     public void teach () {
10         System.out.println("teach");
11     }
12 }
1 package myPackage;
2 import org.springframework.context.annotation.ComponentScan;
3
4 @ComponentScan("myPackage")
5 public class ScanConfig {
6 }

1、洞悉启动容器时的准备工作

熟悉一些Spring的道友应该都知道,refresh方法中的invokeBeanFactoryPostProcessors方法实现了对BeanFactoryPostProcessor实现类的处理。大家如果只看invokeBeanFactoryPostProcessors方法的话,不会发现有何异常之处,此方法虽然较长,但是处理逻辑很清晰,先对重写了BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor方法的实现类进行处理,后对重写了BeanFactoryPostProcessor的方法的实现类做了处理。但是如果心细的话,你会发现问题,Spring是如何将@ComponentScan("myPackage")注解发挥作用的?这时带着这样的问题,我们再回过头来看容器的构造方法,就会在这平实的表面下发现意想不到的 "杀机"。

1 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
2         this();
3         register(annotatedClasses);
4         refresh();
5     }

通过这个构造方法可以知道,在第二行将我们的测试类ScanConfig 注册进了容器中,但这只是注册,注册之后是如何调用如何实现了@ComponentScan("myPackage")这个注解的包扫描的呢?这时我们将目光锁定this()方法。点进去后发现是这样的:

1 public AnnotationConfigApplicationContext() {
2         this.reader = new AnnotatedBeanDefinitionReader(this);
3         this.scanner = new ClassPathBeanDefinitionScanner(this);
4     }

在第二行新建reader对象时,调用了这个构造方法:

1 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
2         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
3         Assert.notNull(environment, "Environment must not be null");
4         this.registry = registry;
5         this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
6         AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
7     }

其中的第六行,最终调用了AnnotationConfigUtils#registerAnnotationConfigProcessors方法,而就是在这个方法中完成了对多个重要Bean的注册,跟我们关系比较大的有以下几个:

 1         // BeanDefinitionHolder只是存放BD的,里面有三个属性:BD对象、beanName以及别名组成的String[]
 2         Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
 3         // 注册最关键的类,对应的类为ConfigurationClassPostProcessor,父类的父类是BeanFactoryPostProcessor
 4         if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
 5             RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
 6             def.setSource(source);
 7             // 将BD注入进容器中,没经过什么处理,只是放入了DefaultListableBeanFactory中的beanDefinitionMap跟存放beanName的list中
 8             beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
 9         }
10         // 此类实现了BeanPostProcessor,用于处理@Autowired、@Value注解
11         if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
12             RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
13             def.setSource(source);
14             beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
15         }
16         // 此类也实现了BeanPostProcessor,用于处理有@Required注解的方法
17         if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
18             RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
19             def.setSource(source);
20             beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
21         }

其中第一个对应的类就是我们重点关注的对象 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类中的下面方法中

1     public static void invokeBeanFactoryPostProcessors(
2             ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {}

先略过开始对手动添加进去的beanFactoryPostProcessors处理逻辑,看后面的部分代码(由于此方法代码较多,此处就不全部粘贴出来了,因为逻辑很好理解,所以只粘贴重点):

 1             String[] postProcessorNames =
 2                     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
 3
 4             // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
 5             List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
 6             // 1.2 先处理实现了PriorityOrdered的类
 7             for (String ppName : postProcessorNames) {
 8                 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
 9                     // 此处通过getBean来生成ConfigurationClassPostProcessor实例对象
10                     priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
11                     processedBeans.add(ppName);
12                 }
13             }
14             // 就一个对象,有啥好排序的
15             sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
16             registryPostProcessors.addAll(priorityOrderedPostProcessors);
17             // 执行ConfigurationClassPostProcessor中的重写方法postProcessBeanDefinitionRegistry,会将所有加了注解的类注册到容器中
18             // 此处才是整个invokeBeanFactoryPostProcessors方法的核心所在,需要详述 下面进入ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry中一探究竟
19             invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

debug到第一行的时候,会发现此处获取到的postProcessorNames 中只有一个值,就是前面准备工作中通过硬编码往容器里注册的ConfigurationClassPostProcessor类。下面的逻辑就是进行各种判断,最后在第19行完成了对ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry方法的调用。

就是在这个后置处理方法中,完成了@ComponentScan("myPackage")中对包的扫描,完成了所有Bean的注册。执行完这个方法后,你会发现beanDefinitionMap中所有应该容器管理的类全都齐活了,包括其他的后置处理器。这样,后面继续调用beanFactory.getBeanNamesForType方法时,获取到的是所有满足条件的类,后面的工作就会有条不紊的开展下去了。

总结

本文着重追溯了BeanFactoryPostProcessor及其子接口是如何在Spring中发挥作用的。先通过Spring初始化容器时注册进去的ConfigurationClassPostProcessor类触发对其他类的扫描,待全部注册进容器后,再从容器中取对应的BeanFactoryPostProcessor及其子接口的实现类,逐一对重写方法进行调用。

虽然魔鬼在细节,但这也正是解读源码的快乐之处,不是吗?

原文地址:https://www.cnblogs.com/zzq6032010/p/11031214.html

时间: 2024-11-07 15:38:06

Spring源码解读之BeanFactoryPostProcessor的处理的相关文章

Spring源码解读之XmlBeanFactory

首先感谢<Spring源码深度解析>郝佳.接下来的Spring源码解读系列,都是读了郝佳的书后的观后感.再次感谢他,带我走进了源码的世界. BeanFactory factory= new XmlBeanFactory (new ClassPathResource("D:\\Project\\Eclipse\\Spring_Maven\\src\\main\\resources\\spring_beans.xml" )); new ClassPathResource(Str

spring 源码解读与设计详解:2 BeanFactory

在spring的官网中我们看到,spring的产品已经发展的非常壮大,然而很多产品对于很多公司来讲用的非常少,甚至用不到.因此本系列的源码解读也不会涉及全部的spring的产品.而是只对spring的核心功能IoC和AOP进行解释. 所谓源码解读,解读的是什么?实际上源码解读读的更多的是源码的注释,因为一个类的作用.一个接口或者一个方法的作用,我们往往是要根据注释才知道,这也是为什么在代码规范中,注释是一个非常重要的模块的原因. 参考: Spring源码分析--BeanFactory体系之接口详

Spring源码解读之核心容器上节

Spring架构图 说明 Spring的流行程度就不用我来说了,相信大家如果使用JAVA开发就一定知道它.写这篇文章的初衷在于:1.了解Spring底层实现原理,提升对Spring的认识与理解.2.学习优秀框架编程实现,学习优秀的设计模式.3.使用Spring三年多,对于底层细节希望知道更多,便于求职. 对于Spring整个架构是很庞大的,很难一下看完和思考完,所以我会从Core Container进行切入,一步一步往上走,从而解开Spring神秘的底层面纱.同时对Spring的IOC\AOP\

spring源码解读之 JdbcTemplate源码

在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,有许多类似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看来这是Rod.Johnson的惯用手法, 所谓模板板式,就是在父类中定义算法的主要流程,而把一些个性化的步骤延迟到子类中去实现,父类始终控制着整个流程的主动权,子类只是辅助父类实现某些可定制的步骤. 我们用代码来说

spring 源码解读与设计详解:3 FactoryBean

上一篇文章讲到BeanFactory,BeanFactory是实现spring IOC原理实现的根接口,而本篇所讲的FactoryBean则是AOP原理实现的重要接口. 1. 先看FactoryBean的源码: public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); boolean isSingleton(); } 2. 下面简单讲一下FactoryBea

Spring源码解读之核心容器下节

续 上一篇我们通过ClassPathXmlApplicationContext加载xml文件,通过BeanFactory获取实例bean的demo代码去解读了Spring Core Container中的spring-beans,spring-core,spring-context三个组件之间的一些具体类的实现.从加载XML.构造BeanFactory.到初始化Bean,已经有了一个全貌的了解.今天继续前一节,我们来说一下注解方式是如何实现bean是如何实现自动化装配和依赖加载的. 注解demo

Spring 源码解读 推荐流程

Spring源代码解析(一):IOC容器:http://www.javaeye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.javaeye.com/topic/86594 Spring源代码解析(三):Spring JDBC:http://www.javaeye.com/topic/87034 Spring源代码解析(四):Spring MVC:http://www.javaeye.com/topic/87692 Sprin

spring源码解读-aop

aop是指面向切面编程,ProxyFactoryBean是spring aop的底层实现与源头,为什么这么说呢?首先我们看一段配置: 1.target是目标对象,需要对其进行切面增强 2.proxyInterfaces是指代理对象所实现的接口 3.interceptorNames:是指通知器(Advisor)列表,通知器中包含了通知advice与切点pointcut 概括一下,ProxyFactoryBean的作用是: 针对目标对象来创建代理对象,将对目标对象方法的调用转到对相应代理对象方法的调

Spring源码之SimpleAliasRegistry解读(一)

Spring源码之SimpleAliasRegistry解读(一) 阅读spring源码中org.springframework.core.SimpleAliasRegistry类时发现该类主要是使用map作为alias的缓存,并对接口AliasRegistry进行实现. 在阅读该源码中对hasAlias(String name, String alias):方法产生了疑惑.源码注释中解释该方法的作用是:确定给定的名称是否已注册了给定的别名. 该方法源码如下: public boolean ha