背景知识
两个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);
也是返回了RootBeanDefinition
ChildBeanDefinition
只比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
和@PreDestroy
BeanFactoryPostProcessor
, 在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类型