背景知识
两个Map
Map<String, BeanDefinition> beanDefinitionMap,声明在DefaultListableBeanFactory,这个属于IOC容器的源头,所有注册的BeanDefinition都在这里,占位符处理器PlaceholderConfigurer也是对这个Map处理.
Map<String, RootBeanDefinition> mergedBeanDefinitions,声明在AbstractBeanFactory,顾名思义里面存放的是"融合"的BeanDefinition,即加工后衍生出来的BeanDefinition
三种BeanDefinition
RootBeanDefinition,GenericBeanDefinition和ChildBeanDefinition都继承于AbstractBeanDefinition
RootBeanDefinition完善了AbstractBeanDefinition的很多信息,熟悉的final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);也是返回了RootBeanDefinitionChildBeanDefinition只比AbstractBeanDefinition多了个parentName,并且所有ChildBeanDefinition的构造方法都需要指定parentName,可以看出它是专为继承而设计的GenericBeanDefinition也是只多了parentName,但它的构造方法无需指定parentName,在Spring 2.5之后都建议使用GenericBeanDefinition来注册自定义BeanDefinition
特别注意的是getMergedLocalBeanDefinition()返回的都是RootBeanDefinition,即经过父子继承,融合加工后都成为RootBeanDefinition存放在mergedBeanDefinitions中
关于BeanDefinition的继承可以参考代码段
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册parentBeanDefinition
GenericBeanDefinition parentBeanDefinition = new GenericBeanDefinition();
parentBeanDefinition.setBeanClass(ParentChlidBeanDefinition.class);
MutablePropertyValues parentPropertyValues = new MutablePropertyValues();
parentPropertyValues.addPropertyValue("name", "ypq");
parentBeanDefinition.setPropertyValues(parentPropertyValues);
applicationContext.registerBeanDefinition("parent", parentBeanDefinition);
// 注册ChildDefinition
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setParentName("parent");
childBeanDefinition.setBeanClass(ParentChlidBeanDefinition.class);
MutablePropertyValues childPropertyValues = new MutablePropertyValues();
childPropertyValues.addPropertyValue("age", 10);
childBeanDefinition.setPropertyValues(childPropertyValues);
applicationContext.registerBeanDefinition("child", childBeanDefinition);
applicationContext.refresh();
ParentChlidBeanDefinition parent = applicationContext.getBean("parent", ParentChlidBeanDefinition.class);
ParentChlidBeanDefinition child = applicationContext.getBean("child", ParentChlidBeanDefinition.class);
// 父BeanDefinition没有age
Assert.assertEquals("ypq", parent.getName());
Assert.assertNull(parent.getAge());
// 子BeanDefinition有age
Assert.assertEquals("ypq", child.getName());
Assert.assertEquals(10, child.getAge().intValue());
关于这部分背景知识也可参考cloud.tencent.com/developer/a…
applicationContext的refresh()
三个常用的PostProcessor
BeanPostProcessor, 在Bean实例化时执行,如CommonAnnotationBeanPostProcessor负责@PostConstruct和@PreDestroyBeanFactoryPostProcessor, 在refresh()中段执行,例如PropertySourcesPlaceholderConfigurer,处理BeanFactory的PropertySourcesBeanDefinitionRegistryPostProcessor, 继承BeanFactoryPostProcessor, 具有更高优先级, 动态引入更多的BeanDefinition
三者在refresh()中的顺序和关系如下图所示
我们知道,如果不合理使用Spring会产生莫名其妙的问题,例如提早实例化Bean就是一种常见的原因.总结一下这类问题,可以分为若干种场景
问题一 提早至BeanPostProcessor实例化阶段
@Configuration
public class EarlyConfiguration {
@Bean
public BeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
}
现象:打印EarlyConfiguration$$EnhancerBySpringCGLIB$$f48a5b2 is not eligible for getting processed by all BeanPostProcessors
分析:日志提示EarlyConfiguration未被所有BeanPostProcessor处理.注意上图绿色的BeanPostProcessor实例化阶段,Spring为了实例化MyBeanPostProcessor,必须先实例化EarlyConfiguration, 此时至少有一个BeanPostProcessor未实例化,更谈不上被BeanPostProcessor处理,因此打印上述日志.
解决:public static BeanPostProcessor myBeanPostProcessor(),加上static后myBeanPostProcessor()不再依赖EarlyConfiguration,也不会导致EarlyConfiguration提早实例化
问题二 提早至BeanFactoryPostProcessor阶段
@Configuration
public class EarlyConfiguration {
@PostConstruct
public void init() {
System.out.println("EarlyConfiguration init...");
}
@Bean
public BeanFactoryPostProcessor myBeanFactoryPostProcessor() {
return new MyBeanFactoryPostProcessor();
}
}
@Bean method EarlyConfiguration.myBeanFactoryPostProcessor is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface.This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class.Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
现象:打印上述日志,没有打印EarlyConfiguration init..., 即@PostConstruct没有执行
分析:Spring为了实例化MyBeanFactoryPostProcessor,导致EarlyConfiguration提前实例化(在图中的蓝色阶段),此时BeanPostProcessor(例如CommonAnnotationBeanPostProcessor)尚未实例化,所以他负责的@PostConstruct也不会执行
解决:public static BeanFactoryPostProcessor myStaticBeanFactoryPostProcessor(), static关键字避免了依赖关系.
问题三 提早至BeanDefinitionRegistryPostProcessor阶段
@Configuration
public class EarlyConfiguration {
@Bean
public BeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor() {
return new MyBeanDefinitionRegistryPostProcessor();
}
}
Cannot enhance @Configuration bean definition 'earlyConfiguration' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
现象:打印上述日志, EarlyConfiguration虽然是Full模式但并没有增强(即不是EarlyConfiguration$$EnhancerBySpringCGLIB$$f48a5b2),只是一个Lite模式(普通)的Configuration
分析:Spring为了实例化MyBeanDefinitionRegistryPostProcessor, 导致EarlyConfiguration提前实例化(在图中的紫色阶段),而ConfigurationClassPostProcessor#postProcessBeanFactory(即增强实现方法enhanceConfigurationClasses())在BeanDefinitionRegistryPostProcessor实例化后和BeanFactoryPostProcessor实例化前执行,此时已经生成了Lite模式的EarlyConfiguration实例, 无法再增强了.
解决: public static BeanDefinitionRegistryPostProcessor myStaticBeanDefinitionRegistryPostProcessor(), static关键字避免了依赖关系.
问题四 getObjectType返回null的FactoryBean
FactoryBean的getObjectType依赖@Value("${car-factory.brand}")确定类型
@Configuration
public class EarlyConfiguration {
@Bean
public BeanFactoryPostProcessor carBeanFactoryPostProcessor(ConfigurableEnvironment configurableEnvironment) {
return new MyBeanFactoryPostProcessor();
}
@Bean
public static FactoryBean carFactory() {
return new CarFactory();
}
}
public class CarFactory implements FactoryBean, InitializingBean {
@Value("${car-factory.brand}")
private String brand;
// ...
@Override
public Class<?> getObjectType() {
if (brand == null) {
return null;
}
try {
clazz = this.getClass().getClassLoader().loadClass(brand);
} catch (ClassNotFoundException e) {
// ignore
}
return clazz;
}
@Override
public void afterPropertiesSet() throws Exception {
LOGGER.error("the brand is {}", brand);
}
}
现象:打印"the brand is null, 属于问题二的特殊情况
分析:首先我们了解下getBean流程的BeanDefinition变化,以methodValidationPostProcessor为例,它需要一个构造参数,调用getBeanNamesForType获取匹配这个参数类型的BeanName, 如果FactoryBean#getObjectType无法确定类型,Spring会完全初始化FactoryBean尝试确定类型.
图中
getMergedLocalBeanDefinition的逻辑:先查mergedBeanDefinitions,有则直接返回,没有则从根据传入BeanDefinition的parent等关系,生成一个"融合"的BeanDefinition存入mergedBeanDefinitions,有点类似于缓存.
回到我们的案例,propertySourcesPlaceholderConfigurer是用来替代占位符的BeanFactoryPostProcessor,属于PriorityOrderd,它的postProcessBeanFactory会修改BeanDefinitionMap,占位符被替换为实际值.此时通常mergedBeanDefinitions已经缓存了一份BeanDefinitionMap,因此此修改不会即时生效.必须在BeanFactoryPostProcessor实例化和调用阶段结束后(图中蓝色部分),调用clearMetadataCache清理mergedBeanDefinitions才会生效.
但是自定义的carBeanFactoryPostProcessor需要构造参数, 导致carFactory在myBeanFactoryPostProcessor实例化时一起实例化,早于clearMetadataCache,因此carFactory打印出null
总结
概括来说,Spring的提前初始化可以分为三大类,分别是BeanPostProcessor(绿色),BeanFactoryPostProcessor(蓝色),BeanDefinitionRegistryPostProcessor(紫色)三个阶段都有可能提前初始化,特别地如果FactoryBean的getObjectType返回null,也可能导致其提前初始化.掌握好以下两点避坑指南:
PostProcessor用static关键字FactoryBean尽早明确Bean类型