1. BeanFactoryPostProcessor调用过程源码剖析
2. 配置类的解析过程源码
3. 配置类@Configuration加与不加的区别
4. 重复beanName的覆盖规则
5. @ComponentScan的解析原理
我们经常会在一个类上打上@Configuration, @Component, @Bean等. 带有这些注解的类, 就是我们所说的配置类. 那么, spring启动的时候,是如何加载这些配置类的呢?
下面就以此为目的, 分析spring源码. 本节的内容是对上一节内容的实战分析, 同时更加详细的解读spring源码

我们知道, spring启动的时候做了3件事, 就是上面的三件事.
我们先定义好要分析加载的配置类
这个配置类很简单, 使用@ComponentScan注解指定了扫描的包. @Configuration指定当前是一个配置类
在main里, 通过AnnotationConfigurationApplicationContext读取配置类MainConfig.class.
配置类被传进来以后, 到底是怎么被解析的呢? 这就是我们分析的线索
始终不要忘记我们的整体架构图. 对照这个图来分析. 思路更清晰. 整体内容讲解在这里: https://www.cnblogs.com/ITPower/p/13677635.html
下面, 从入口进入. 我们的入口就是这里
下面进入AnnotationConfigApplicationContext的构造方法
在初始化AnnotatedBeanDefinitionReader(this);的时候, 注册了很多后置处理器
我们看到, 注册了6个原始RootBeanDefinition, 这些bean是spring自己提前定义好的, 他们的加载是整个spring的基础. 用于解析spring中其他的类
而这一次我们要研究配置类是如何被读取的, 所以重点关注的是下面这个后置处理器
这里还有很多其他的原始类被注册了, 但我们的目标是分析配置类是如何被读取的, 所以, 其他的先忽略, 只看ConfigurationClassPostProcessor.
可以看到ConfigurationClassPostProcessor是同时实现了BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor. 这一点我们需要记住, 后面会使用到
首先,构建了一个RootBeanDefinition. 然后调用了registerPostProcessor方法, 三个入参分别是
这里面的关键代码是标红的部分, 将ConfigurationClassPostProcessor放入到了beanDefinitionMap里面
下面的else是处理循环引用的问题, 暂时先不要看.
在this()构造方法里, 还初始化了ClassPathBeanDefinitionScanner, 这里只说一句.
我们在扫描配置类的时候, 确实使用的是ClassPathBeanDefinitionScanner, 但是, 不是this.scanner对象. 而是自己new的一个ClassPathBeanDefinitionScanner.
这里的scanner仅仅是为了程序员可以手动调用AnnotationConfigApplicationContext对象的scan方法
通过调用context.scan("package name");扫描处理配置类
使用方式如下:
到目前为止完成了后置处理器注册为BeanDefinition
备注:
ConfigurationClassPostProcessor是一个工具类, 这个类的作用是解析配置类.
工具类有了, 那么还得有主角呀, 那就是我们上面的配置类. 下面看看配置类的加载
注册配置类,入口自然是这里了
跟踪进去找到doRegisterBean(...)方法
重点就是红色这句话, 其他可以略过, 因为我们的配置类很简单, 直接看BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
我们找到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());方法, 进入到DefaultListableBeanFactory查看方法, 这个方法之前我们已经调用过一次
就是在注册ConfigurationClassPostProcessor的时候, 我们需要将其解析为BeanDefinition然后放到BeanDefinitionMap中, 这里也是一样的, 将我们的配置类MainConfig解析成BeanDefinition放入到BeanDefinitionMap中.
这里的代码在整个框架中处于什么位置呢? 将MainConfig解析为BeanDefinition放入到BeanDefinitionMap中
以上两步, 一个是将ConfigurationClassPostProcessor配置类后置处理器, 也就是解析配置的工具类, 解析成BeanDefinition放入到BeanDefinitionMap中
另一个是将我们的目标配置类MainConfig加载到内存, 组装成BeanDefinition放入到BeanDefinitionMap中.
到这里,我们完成了两步.
第一步: 准备工具类ConfigurationClassPostProcessor
第二步: 准备配置类MainConfig.
接下俩, 就是要使用工具类来解析配置类MainConfig了
在refresh()中有很多步骤, 我们重点来看invokeBeanFactoryPostProcessors(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);看名字, 调用的是Bean工厂的后置处理器, 上面分析了, 初始化的时候初始化了很多spring原生的后置处理器, 这么多后置处理器, 其实, 只有一个后置处理器实现了BeanFactoryPostProcessor, 它就是ConfigurationClassPostProcessor, 还记得上面的结构图么, 拿下来, 再看一遍.
这里调用的时候, 原生处理器只会调用ConfigurationClassPostProcessor
这里要调用bean工厂的后置处理器了. 看上面的注释, 注释写的很清晰.
在调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());的时候调用了getBeanFactoryPostProcessors()方法.
接下来重点来了. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 方法实现一共分为两大步:
来看看源码是如何定义的. 重点看代码的注释, 每一部分的功能都有明确标出, 注释写的很详细
下面我们就来分析上图所示的内容.
首先, 拿到了所有实现了BeanDefinitionRegistryPostProcessor的后置处理器, 上面我们做过铺垫,只有ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor后置处理器
所以,这里过滤出来的postProcessorNames只有一个,就是ConfigurationClassPostProcessor, 接下来, 判断这个类是否实现了PriorityOrdered 优先排序的接口, 如果实现了, 那么放入到currentRegistryProcessors中, 后面会进行调用.
接下来, 执行invokeBeanDefinitionRegistryPostProcessors
这是第一次调用BeanDefinitionRegistryPostProcessors
第二次调用的时候 ,依然是获取所有的实现了BeanDefinitionRegistryPostProcessor接口的后置处理器, 且这个处理器没有实现过PriorityOrdered也就是没有被上面调用过. 且实现了Ordered接口
这一类添加到currentRegistryProcessors集合中, 然后调用invokeBeanDefinitionRegistryPostProcessors处理
这是第二次调用BeanDefinitionRegistryPostProcessor
第三次调用的是没有实现过任何排序接口的后置处理器. 并将其放入到currentRegistryProcessors, 然后执行invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor同时实现了BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessors, 调用的是invokeBeanFactoryPostProcessors
一共进行了4次调用
总结: 优先处理的是实现了PriorityOrdered的后置处理器, 然后调用实现了Order接口的后置处理器, 最后调用了没有实现任何排序方法的后置处理器. 最后调用工厂类方法.
下面我们来具体分析invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
我们在这一步打个断点, 然后跟着断点一步一步点击进去
这是registryProcessors里面只有一个后置处理器, 就是ConfigurationClassPostProcessor.
然后进入到ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法
这里先看enhanceConfigurationClasses(beanFactory);个方法
粗体部分就是判断是否需要进行cglib代理. 进行cglib代理的条件是, beanDefinition中属性configurationClass的值是full. 只有full版配置才会创建cglib代理
那么有下面几个问题:
问题1: full版本配置是什么呢?
我们使用@Configuration注解了, 在加载的时候, 就会将configurationClass属性设置为full.当设置为full以后, 我们在调用的时候, 就会创建一个cglib动态代理.
问题2: 为什么要创建动态代理呢?
动态代理可以保证, 每次创建的bean对象只有一个
问题3:那么加@Configuration和不加本质上的区别是什么?
当在配置类中一个@Bean使用方法的方式引入另一个Bean的时候, 如果不加@Configuration注解, 就会重复加载Bean.如果加了@Configuration, 则会在这里创建一个cglib代理, 当调用了@Bean方法是会先检测容器中是否存在这个Bean, 如果不存在则创建, 存在则直接使用.
这是在上面调用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);接口的时候, 标记的是full还是Lite
下面来看一下源码
在这里一步,执行的时候,进行了这个类是full的还是lite,继续忘下看
此时满足条件的postProcessor只有一个, 那就是ConfigurationClassPostProcessor. 下面直接看ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()方法
前面都是一些条件判断, 重点看processConfigBeanDefinitions(registry);
在这里,这个方法判断了, 这个类是full的还是lite的. 下面直接上代码
上面主要是获取元数据, 然后判断元数据中是否有Configuration注解. 如果有,返回其属性. 我们判断其属性中proxyBeanMethods是否true, 如果是true, 那么将其设置为full.
如果配置中带有@Component, @ComponentScan @Import @ImportResource @Bean这几种属性之一, 那么就将其设置为lite.
这也是@Configuration 和其他注解类似@Component和@ComponentScan的本质区别:
当在配置类中一个@Bean使用方法的方式引入另一个Bean的时候, 如果不加@Configuration注解, 就会重复创建Bean
如果加了@Configuration, 则会在这里创建一个cglib代理, 当调用了@Bean方法是会先检测容器中是否存在这个Bean, 如果不存在则创建, 存在则直接使用.
下面来看个例子
这是定义的car和tank的基础类
当配置类使用了@Configuration注解的时候, 运行main方法
当去掉@Configuration注解的时候, 再次运行, 我们看到创建了两次tank
在main方法中调用了两次(Car) context.getBean("car");
在new一个对象的时候, 如果不取ioc容器中取, 那么每一次都会创建一个新的.
在ioc容器中, car对象只有一个, 但是在构建car的时候, 调用了tank, tank在ioc容器中却不一定只有一份. 只有使用了@Configuration, 表示需要使用cglib动态代理查找tank类, 保证ioc容器中只有一份.
通过跟踪@ComponentScan注解是如何解析的, 分来理解BeanDefinitionScan, BeanDefinitionRegistry, BeanDefinitionReader是如何工作的.
这里也有两大步
第一步: 初始化bean工厂的后置处理器
通过调用beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class) 初始化了bean工厂的后置处理器,
第二步: 解析配置
调用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);调用beanDefinitionRegistry的后置处理器. 筛选出符合条件的配置类.
如上图所示, 最后筛选出的配置类只有MainConfig配置类. 也就是说configCandidates配置候选集合中只有一个MainConfig
然后, 接下来创建了一个对象ConfigurationClassParser, 这是一个配置类解析器. 下面将使用这个解析器解析配置类.
重点是如何解析的, 代码已重点标注出来了.
我们这里是通过注解解析的, 所以直接看下面的代码
解析主要做了几件事呢?如下图:
解析配置类, 看看配置类是否含有如上标记的注解, 如果有, 则调用响应的返回对其进行解析,处理.
下面来看看源码. 是如何处理这一块的.
下面我们重点看对@ComponentScan和@ComponentScans注解的解析, 为什么看他呢? 因为很多注解都标记了@Component注解.
比如@Service注解,本身使用@Component
再来看@Controller注解, 其实质也是一个@Component注解
我们在自定义配置类的时候, 会使用@ComponentScan注解. 并传递一个包, 作为扫描包. 如MainConfig配置
这就会扫描包下所有的配置类.
它主要的逻辑如下:
在拿到@ComponentScan注解以后, 会对其进行parse. 主要解析里面的注解. 并对每一个注解进行处理. 处理后将其添加到scanner属性中. 最后调用scanner.doScan(....)方法.
源码如下:
调用doScan方法扫描配置类. 我们来看看主要做了哪些事情
第一步: 找到所有候选的BeanDefinition.
上面解析出了@ComponentScan注解传递过来的basePackages包. 扫描包中所有的类, 得到候选类.
扫描的时候做了几件事呢? 看最上图最右侧部分. 这扫描出来就是我们的目标类.
第二步: 解析这些准目标类.
第三步: 设置默认的beanDefinition属性
第四步: 将解析出来的bean定义注册到ioc容器中
这里就调用了BeanDefinitionReaderUtils.registerBeanDefinition注册bean定义. 之前注册过配置类, 这里和其是一样的. 所以不再赘述了
这里有两个细节:
1. excludeFilter中排除了自己
在解析配置类的时候, 除了@ComponentScan注解中定义的ExcludeFilter和IncludeFilter以外, 还有默认的排除类. 如上加粗字体的部分. 这里是排除了配置类本身, 我们这里的配置类是MainConfig, 也就说, 会排除掉自己.
matchClassName是一个钩子方法. 在执行到这里的时候, 不会真的去执行. 什么时候执行呢? 后面调用doScan的时候执行.
在寻找候选配置类的时候, 进行了排除了配置类本身.
进入这个方法
在第四步的时候调用了isCandidateComponent(metadataReader, 这里就判断了是否是包含的类,或者是排除的类
紫色加错的部分是就是判读是否符合排除的类. 红色加错的部分是判断是否是包含的类.
先来看紫色的部分, 排除的类
看到了么, 这里调用了matchClassName. 这就是上面定义的钩子方法,
此时declaringClass表示的是当前的配置类, className表示的是目标类, 如果当前目标类 == 配置类, 那么就放回true. 返回true, 则会排除掉
2. includeFilter中包含了默认的配置类
下面来看红色加错的部分
我们看到这里有this.includeFilters.包含的过滤器. 这里面是有值的
我们没有在配置类MainConfig上设置includeFilter啊, 这里面怎么会有值呢?
这是因为我们有默认包含的过滤器, 下面看看默认包含的过滤器是在哪里设置的.
首先从入口类点击AnnotationConfigApplicationContext
然后在点击this();
再点击ClassPathBeanDefinitionScanner
然后一路点击三个this(...)
最后看到上图 registerDefaultFilter();注册默认的过滤器
如上图看到, 注册了3个默认的过滤器. 分别是Component, ManagedBean, Named. 他们都是注解类型的过滤器AnnotationTypeFilter
其中javax.annotation.ManagedBean和javax.inject.Named是jdk提供给我们的.