项目启动过程中,控制台打印了如下日志
"Cannot enhance @Configuration bean definition 'xxx' 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'."
意思是: 无法增强@Configuration注解得名字为xxx的bean,原因是这个类被实例化太早了,最常见的原因就是在配置类中使用@Bean标记了一个BeanDefinitionRegistryPostProcessor接口的实现类.考虑使用static去修改这个方法。
1 、本人并没有像警告信息里说的那样在一个配置类中使用@Bean去注入BeanDefinitionRegistryPostProcessor的实现类,但是导致抛出这个警告的原因是一样的.
先看代码复现下该警告,只需要如下代码即可:
@Configuration
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
public MyBeanDefinitionRegistryPostProcessor() {
System.out.println();
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
System.out.println();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println();
}
}
通过日志可以看出,该信息在org.springframework.context.annotation.ConfigurationClassPostProcessor中打印,下面来看一下为什么会打印这个警告?
首先ConfigurationClassPostProcessor 实现了接口BeanFactoryPostProcessor, 并在方法postProcessBeanFactory中对所有的@Configuration类进行cglib增强,动态生成配置类的子类,在实例化配置类时是通过子类创建的Bean实例.
full config就是被@Configuration注解标记的配置类, 但是如果配置类在增强之前就被调用getBean()实例化了,就会打印我们所看到的警告信息.
那么我们的MyBeanDefinitionRegistryPostProcessor
为什么会在增强之前被实例化呢?
原因就在于其实现了接口BeanDefinitionRegistryPostProcessor,该接口是
BeanFactoryPostProcessor的子类,当调用org.springframework.context.support.AbstractApplicationContext refresh()方法时会调用invokeBeanFactoryPostProcessors()而该方法中会优先通过getBeanNamesForType() 找到所有的BeanDefinitionRegistryPostProcessor实现类并通过getBean创建bean实例,并遍历调用接口方法
postProcessBeanDefinitionRegistry, 其优先于父类接口
BeanFactoryPostProcessor得实例执行, 而@Configuration配置类的增强是在BeanFactoryPostProcessor的实现类ConfigurationClassPostProcessor的postProcessBeanFactory方法中执行的,这就导致了MyBeanDefinitionRegistryPostProcessor
被提前实例化了, 所以出现了我们所看到的警告信息.
这个警告信息一般情况下不影响我们的程序运行,当我们仅需要使用的BeanDefinitionRegistryPostProcessor实现类,不需要复杂的配置信息时,可以通过使用@component注解来替换
@Configuration.
2、警告中所说的考虑使用static修饰方法是什么情况呢?如下代码其实也会导致出现我们看到的警告信息
@Configuration
public class MyConfig {
public MyConfig() {
System.out.println();
}
@Bean
public BeanDefinitionRegistryPostProcessor myBeanDefRegPostProcessor() {
return new MyBeanDefinitionRegistryPostProcessor();
}
// 使用static方法则不会打印警告
// @Bean
// public static BeanDefinitionRegistryPostProcessor myBeanDefRegPostProcessor() {
// return new MyBeanDefinitionRegistryPostProcessor();
// }
}
原因是当获取BeanDefinitionRegistryPostProcessor接口的bean时,会调用myBeanDefRegPostProcessor()方法创建Bean,而调用该方法前同样需要先实例化MyConfig,导致了MyConfig在被增强之前被实例化了, 而如果把方法修饰为static,static方法属于类方法,就不会触发MyConfig被提前实例化也就不会出现警告信息了.