invokeBeanFactoryPostProcessors 在 Spring 启动流程中,是非常重要的一个步骤,此阶段会实例化并调用所有注册的 BeanFactoryPostProcessor beans。
方法有两个入参,分别是 工厂 beanFactory 和 初始的List<BeanFactoryPostProcessor>。
1 | public static void invokeBeanFactoryPostProcessors( |
BeanDefinitionRegistryPostProcessor
在上面的代码中,逻辑比较清晰,主要有两步:找出所有BeanDefinitionRegistryPostProcessor,并按优先级依次调用;找出所有 BeanFactoryPostProcessor 并按优先级依次调用。
那 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 有什么区别呢?这父子俩源码如下:
1 |
|
BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,根据源码,其执行时机也是早于 BeanFactoryPostProcessor 的,在 自定义BeanFactoryPostProcessor的错误做法 中有介绍过 BeanFactoryPostProcessor 接口。
简而言之, BeanFactoryPostProcessor 允许你修改 BeanDefinition,BeanDefinitionRegistryPostProcessor 允许你动态的注册 BeanDefinition。
举个代码演示下:
1 | class Cat { |
很明显, Cat 类上并没有添加任何 Spring 的注解,就是个简单的 bean 类,但是我们却可以通过自定义的 MyBeanDefinitionRegistryPostProcessor 来注册一个名字叫 cat 的 Cat类型的 beanDefinition,并在之后的过程中实例化这个对象。
ConfigurationClassPostProcessor——最核心的类
我们可以自定义 BeanDefinitionRegistryPostProcessor 并注册一些 beanDefinition 的,执行完所有的 BeanDefinitionRegistryPostProcessor 后,继续执行所有的 BeanFactoryPostProcessor 接口,最后根据 beanDefinition 实例化对应的 bean。
但是否思考过 spring 是如何找到我们自定义的 BeanDefinitionRegistryPostProcessor 呢?就是靠 ConfigurationClassPostProcessor 处理器,它是第一个执行的 BeanDefinitionRegistryPostProcessor 处理器,它也 Spring 中是最核心的类,没有之一。
我们常常会用 @Bean、@Component、@Service、@Configuration 等注解来标记一个类是 bean,Spring就是通过 ConfigurationClassPostProcessor 这个处理器来找到所有带这些注解的类的。
那思考一下,ConfigurationClassPostProcessor 自己是什么时候被添加到 beanFactory 的呢?是在初始化 context 容器的时候添加进去的,我们来查看源代码:
在run方法的时候
1 | // 下面这行代码 |
不停地深入this.reader = new AnnotatedBeanDefinitionReader(this);
,最终会调用以下方法,这就是初始化添加一些处理器的地方。
1 |
|
最后,我们再来看看 ConfigurationClassPostProcessor 这个核心类的核心方法。
1 | public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { |
doProcessConfigurationClass 核心方法,每个bean都调用一次
我们先跟进parser.parse(candidates);
方法,一直跟进,最后会进入ConfigurationClassParser.java
中的doProcessConfigurationClass
方法,分析一下这个方法。
1 | protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass( |
processMemberClasses 方法,注册恰好是配置类本身的成员(嵌套)类。
如果有一个类被spring管理了,那它的静态内部类、非静态内部类也都可以被spring管理。
1 | /** |
parse 与 doScan 方法
做一些配置工作,如 若没有指定 basePackages ,则使用启动类的包路径。再看一下 Set<BeanDefinitionHolder> scannedBeanDefinitions=this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName());
这一行中调用的parse方法,看她是如何扫描的
1 | public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { |
scanCandidateComponents 方法,找到某个包路径下的所有候选人
1 | private Set<BeanDefinition> scanCandidateComponents(String basePackage) { |
isCandidateComponent 判断某个类是否是候选人
1 | // 继续跟进核心方法,判断是否是候选人 |
isConditionMatch 方法判断是否满足了 Condition 注解的条件
会找出类上面的所有 Condition ,并依次判断是否满足条件,有一个不满足,就不会将这个类添加为候选人。
1 | private boolean isConditionMatch(MetadataReader metadataReader) { |
isCandidateComponent 方法再次判断是否是候选人
我们继续回到scanCandidateComponents
方法,如果一个类已经被判定为候选人,也就是要被spring管理了,往后走,又进入了一个判断isCandidateComponent(sbd)
,方法名和上面判定是否为候选人一样,是个重载的方法。
1 | protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { |
根据上面的那个方法,如果@Component 注解标记在内部类上,spring是无法找到并管理的,但实际测试,是仍然可以的,这是因为doProcessConfigurationClass
方法里第一步就调用了processMemberClasses(configClass, sourceClass, filter);
方法,用于判断这个类里面是否有成员类可以进行管理,上面有说此方法。
为配置类设置 full 或 lite 模式
到此时所有的需要spring托管的类都已经被找到了,我们又该回到之前说的doProcessConfigurationClass
方法了,上面所说的都是Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
这一行方法调用的具体内容,现在所有需要托管的类都已经在scannedBeanDefinitions
中了,紧接着一次遍历,对每个 bean 进行一次 checkConfigurationClassCandidate
调用,用于判断该bean是否是配置类,只要类上面有Component、ComponentScan、Import、ImportResource,或方法上有@Bean,都是配置类,只不过是 lite 模式的配置类,而如果有@Configuration且proxyBeanMethods属性为true的话,会在beanDef中添加 full 属性,其他都添加 lite 属性。
1 | // 涉及到的相关成员变量 |
@Configuration有什么不一样?为什么?
以下代码两行输出都只会打印一次,且两个bean是同一个,这意味着obj()方法只被调用了一次。
1 |
|
而如果使用的是 @Component 而不是 @Configuration,会很容易发现obj()方法被调用了两次,且两个bean不是同一个对象了。
它们俩的区别是从哪开始产生的呢?就是从上面说到的,标记lite模式或full模式开始。所有有 Configuration 注解且注解的 proxyBeanMethods 属性为true的话,就会标记为full模式,其余为 lite 模式。
我们继续回到 ConfigurationClassPostProcessor 这个类,这个类实现了 BeanDefinitionRegistryPostProcessor 接口,这个接口的 postProcessBeanDefinitionRegistry 方法已经讲解完了,还有 postProcessBeanFactory 未说明,而 @Configuration 这部分功能的实现就在这里,上代码!
1 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
继续跟踪增强的方法。所谓增强,无非就是使用 CGLib 进行动态代理,下面的很多代码也都是属于 CGLib 的了,如果对这个框架不熟悉,建议先简单了解下,跑一个最简单的 demo。
1 | public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) { |
核心逻辑在 BeanMethodInterceptor 这个拦截器里面。它的拦截方法是:
1 | public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, |