此篇博客由于能力有限,虽然有些讲不全,希望对你有所帮助。
先看下该处理器的继承图吧
这个工具类中有两个很重要的属性org.springframework.context.annotation.ConfigurationClassUtils
private static final String CONFIGURATION_CLASS_FULL = "full";
private static final String CONFIGURATION_CLASS_LITE = "lite";
这两个属性怎么解释呢
full 满的,我们可以理解为全部 而spring 中对应的 lite也可以理解为 一部分的
那么我们的主角@Configuration注解和这两个属性有什么关系呢,为什么在配置类中加与不加都是spring环境生效呢
我们先看环境中的代码
@Configuration
//@Import(TestImportSelector.class)
//@TestImportSelectorAnno
//@TestBeanFactoryPostProcessorAnno
@ComponentScan("org.springframework.test.main.*")
public class ScanConfig {
}
//测试
public class MainDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(ScanConfig.class);
ScanConfig scanConfig = (ScanConfig) ac.getBean("scanConfig");
}
}
我们进入该类的这段代码断点进去调试
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
//断点调式
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//给配置类产生cglib代理。加了@Configuration注解的
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
正常情况下这段代码只会进else if 由于spring设计的严谨性,这段代码会解析注册列表中(BeanDefinition)的所有bd,将检测到带有@Configuration注解的类放到另外一个bd集合中进入下次解析。在检测中会将带有该注解的类设置一个属性,就是isFull(简写),没有的就是lite(也间歇)属性,我们跑完到下一个断点解析
该类源码中第二次解析
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' 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'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
这一段代码可以这样理解将带有@Configuration的类放到一个map中,如果为空就直接返回,如果不为空就进入下面的代码,
下面的代码就不帖出来了,等下直接看结果我们就知道了,断点回到测试类。我们看有什么彩蛋
对,cglib代理。如果没加就不是了,直接看结果
为什么呢,这就是spring严谨性的厉害之处之一了(单例性原则)。我们先看下面的一段代码
看下初始化的环境代码
@Configuration
@ComponentScan("org.springframework.test.main.*")
public class ScanConfig {
@Bean
public Test getTestBean(){
return new Test();
}
@Bean
public User getUserBean(){
return new User();
}
}
public class Test {
public Test(){}
}
public class User {
public User(){
System.out.println("init-user");
};
}
public class MainDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(ScanConfig.class);
}
}
加不加该注解,打印的结果都是init-user,如果改一下呢
@Configuration
@ComponentScan("org.springframework.test.main.*")
public class ScanConfig {
@Bean
public Test getTestBean(){
getUserBean();
return new Test();
}
@Bean
public User getUserBean(){
return new User();
}
}
//cglib此结果会打印一次.加入static关键字会失效
@ComponentScan("org.springframework.test.main.*")
public class ScanConfig {
@Bean
public Test getTestBean(){
getUserBean();
return new Test();
}
@Bean
public User getUserBean(){
return new User();
}
}
//此结果会打印两次
但是有个问题,打印的两个hashcode都是一样的,一样是单例的对象
由于水平有限,真正原因也不得而知
两个hashcode一样可能是因为spring在创建对象的时候会先从缓存的单例池(默认是单例,也可以理解为单例池)中拿一次(从缓存的单例池不知道是不是解决循环依赖的问题)第一次,再从真正的单例池去拿(bean的生命周期调用链是这样的步骤)
能力问题,只能后期再补了,也希望对你们有帮助,spring源码太难看了,由于没有证明过,网上的解释也不知道是不是对的(网上的帖子很乱,想找到一个正确的很难找到)。