Spring Bean生命周期&Hook&循环依赖 - ambition0802/spring-practice GitHub Wiki
AbstractApplicationContext implements BeanDefinitionRegistry
javax.annotation.Nonnull
自动注入接口A是,A存在多个实现类,则被@Primary注解的实现类的优先级最高
RootBeanDefinition ChildBeanDefinition ParentBeanDefinition
springboot项目启动的时候,是如何将项目中用户自己设置的@component等自定义的bean扫瞄进容器的。
springboot, 如果没有配置scanner要扫描的包路径,默认是扫描springboot启动类所在的包,实现过程如下:
检查scanningPackages:
- Spring SPI机制加载ApplicationContextInitializer,加载了ConfigurationWarningsApplicationInitializer.
- ConfigurationWarningsApplicationInitializer执行initialize方法,在AbstractApplicationContext的beanFactoryPostProcessors(List)上放入了ConfigurationWarningsPostProcessor.
- 在AbstractApplicationContext的invokeBeanFactoryPostProcessors方法中,执行ConfigurationWarningsPostProcessor的postProcessBeanDefinitionRegistry方法,在方法中,获取BeanFactory中已经存在的AnnotationBeanDefinition(启动类的BeanDefinition就是一个AnnotationBeanDefinition),在AnnotationBeanDefinition上获取@ComponentScan注解的配置,从注解的配置中获取Scaner要扫描的packages,如果没有的话,将Springboot启动类的package添加到packages中。
- 检查packages是否异常。
扫描指定的scanningPackages:
-
invokeBeanFactoryPostProcessors方法中执行BeanFactoryPostProcessor
-
执行ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法。
-
先从BeanDefinitionRegistry中获取所有的BeanDefinition,从这些BeanDefiniton中捞出来被@Configuration注解了的,下文以CBD代指。
-
使用ConfigurationClassParser来解析所有的CBD.
-
ConfigurationClassParser的processConfigurationClass方法从CBD开始递归解析CBD以及被CBD引入的bean(引入方式由多种:@Component、@PropertySource、@ComponentScans、@ComponentScan、@Import、@ImportResource、@Bean)
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { ...... ...... ...... do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
-
ConfigurationClassParser的doProcessConfigurationClass方法来解析每个CBD。解析CBD上面的以下注解:@Component、@PropertySource、@ComponentScans、@ComponentScan、@Import、@ImportResource、@Bean(方法、设置是interface中被@Bean注解的default方法,我直呼牛逼)
-
"Springboot启动过程中如何扫描指定的package并加载package下配置的bean"的答案已经在6中说的很清楚了,通过ConfigurationClassParser读取@Component配置,扫描basePackages包路径(如果没有配置,默认扫描Springboot启动类所在的package)下的配置的Spring bean(包括@Controller、@Service、@Configuration等等),扫描到这些bean之后,再调用ConfigurationClassParser的doProcessConfigurationClass方法进行递归扫描。
几个问题:
- ConfigurationClassPostProcessor 单例 是在什么时候被放到BeanFactory中的? -----> new AnnotationConfigServletWebServerApplicationContext()
问题:
- MergedBeanDefinition到底是什么鸡巴东西。。。
- InstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- InstantiationAwareBeanPostProcessor
- AutowiredAnnotationBeanPostProcessor
不只postprocessor,还有ImportBeanDefinitionRegistrar、各种interface等。
获取a的单例入口 -> org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) L207
先去三级缓存里找a(这个方法典中典要说明下) -> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) L180
三级缓存中都没有a,去实例化a -->
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance L1164
实例化完之后在初始化之前按加入三级缓存中 -> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory L154
初始化a -> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean L1367
自动注入a当中的b property ,获取b的触发入口在 --> org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate L273
获取b的单例入口 , 有没有发现又调用回来这个方法了 -> org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) L207
先去三级缓存里找b(这个方法典中典要说明下) -> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) L180
三级缓存中都没有b,去实例化b -->
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance L1164
实例化完之后在初始化之前加入三级缓存中 -> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory L154 初始化b -> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean L1367 自动注入b当中的a property ,获取a的触发入口在 --> org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate L273
获取a的单例入口 -> org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) L207
先去三级缓存里找a(这个方法典中典要说明下) -> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) L180
按照1-2-3的顺序,从三个级别的缓存中找a,最终在三级缓存中找到a,执行ObjectFactory.getObject(AOP逻辑)来获取a的实例。将a从三级缓存中移动到二级缓存,因为已经执行过”aop逻辑了“
b只有a一个property,设置完之后,再执行其他一些逻辑,比如BeanPostProcessor等逻辑,最终完成初始化。 -> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) L1767
b最终完成了初始化之后,如果开启了AOP,则在执行BeanPostProcessor的postProcessAfterInitialization
时候,会执行AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization,这里面就是创建目标bean的代理对象,来完成aop(代理方式有JDK和CGLIB)
bean完成初始化之后就是一个完整的bean了。如果需要生成代理,在这个时候也早执行过BeanFactoyPostprocessor的postProcessAfterInitialization方法了,早就包装了一层代理对象了,所以直接从三级缓存中移除并添加到一级缓存中,不需要再执行三级缓存中的工厂方法来生成代理了。 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton L137
最后b从三级缓存中拿出来放到一级缓存中, org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton L137
实际上两级缓存就能解决循环依赖的问题。为什么要设置第三级缓存?
spring的aop还有@Transactional的实现原理,其实都是在原本的目标bean对象上,进行代理,封装一层。所以实际上我们的bean在实例化+初始化之后,如果有上述等类似的配置,spring会通过BeanPostProcessor的postProcessAfterInitialzation方法,来对我们的bean进行动态代理来实现功能加强。
但是如果没有第三级缓存,当我们需要动态代理的bean存在循环依赖的话,在初始化后再动态代理我们的bean,就太晚了。举个例子,A需要动态代理,A实例化完之后,依赖B,初始化的时候注入B,创建B实例,B依赖A,B进行初始化的时候注入实例化完但为初始化也未被动态代理的A,B初始化完毕,A初始化完毕,A进行动态代理。
执行完上述这个例子的过程之后,我们可以发现,如果没有第三级缓存,B中持有的A的对象并不是A经过动态代理之后的对象,那么AOP、Transactional等功能就是失效没用的。
所以我们需要有一个Map,来保存已经实例化完且未初始化也未动态代理过的对象。当存在循环依赖的时候,就从这个Map中将对象拿出来,也尝试动态代理一下,再将对象返回。这个Map就是我们的第三级缓存。
提一嘴 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#earlyProxyReferences记录了从第三级缓存中去除提前被aop动态包装了的循环依赖,防止同一个对象进行重复的动态代理。