spring 核心后置处理器-ConfigurationClassPostProcessor

论坛 期权论坛 脚本     
匿名技术用户   2021-1-9 08:38   627   0

此篇博客由于能力有限,虽然有些讲不全,希望对你有所帮助。
先看下该处理器的继承图吧

这个工具类中有两个很重要的属性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源码太难看了,由于没有证明过,网上的解释也不知道是不是对的(网上的帖子很乱,想找到一个正确的很难找到)。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP