spring5 源碼深度解析----- 被面試官給虐懵了,竟然是因為我不懂@Configuration配置類及@Bean的原理


@Configuration注解提供了全新的bean創建方式。最初spring通過xml配置文件初始化bean並完成依賴注入工作。從spring3.0開始,在spring framework模塊中提供了這個注解,搭配@Bean等注解,可以完全不依賴xml配置,在運行時完成bean的創建和初始化工作。例如:

public interface IBean {

}

public class AppBean implements IBean{

}

//@Configuration申明了AppConfig是一個配置類
@Configuration 
public class AppConfig {
    //@Bean注解申明了一個bean,bean名稱默認為方法名appBean
    @Bean 
    IBean appBean(){
        return new AppBean();
    }
}

默認情況下bean的名稱和方法名稱相同,你也可以使用name屬性來指定,如@Bean(name = "myBean")

@Configuration注解使用

我們先來看看@Configuration 這個注解的定義

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //@Component元注解
public @interface Configuration {
    String value() default "";
}

我們看到源碼里面,@Configuration 標記了@Component元注解,因此可以被@ComponentScan掃描並處理,在Spring容器初始化時Configuration類 會被注冊到Bean容器中,最后還會實例化。

使用@Autowired/@Inject

因為@Configuration本身也是一個@Component,因此配置類本身也會被注冊到應用上下文,並且也可以使用IOC的@Autowired/@Inject等注解來注入所需bean。我們來修改配置類如下:

@Configuration
public class AppConfig {
    @Autowired public Environment env;
    @Bean
    IBean appBean(){
        return new AppBean();
    }
}

使用@CompomentScan

配置類也可以自己添加注解@CompomentScan,來顯式掃描需使用組件。

@Configuration 使用@Component 進行原注解,因此@Configuration 類也可以被組件掃描到(特別是使用XML元素)。

@Configuration
@ComponentScan("abc.xxx") public class AppConfig {
    @Bean
    IBean appBean(){
        return new AppBean();
    }
}

在這里認識幾個注解: @Controller, @Service, @Repository, @Component

  • @Controller: 表明一個注解的類是一個"Controller",也就是控制器,可以把它理解為MVC 模式的Controller 這個角色。這個注解是一個特殊的@Component,允許實現類通過類路徑的掃描掃描到。它通常與@RequestMapping 注解一起使用。

  • @Service: 表明這個帶注解的類是一個"Service",也就是服務層,可以把它理解為MVC 模式中的Service層這個角色,這個注解也是一個特殊的@Component,允許實現類通過類路徑的掃描掃描到

  • @Repository: 表明這個注解的類是一個"Repository",團隊實現了JavaEE 模式中像是作為"Data Access Object" 可能作為DAO來使用,當與 PersistenceExceptionTranslationPostProcessor 結合使用時,這樣注釋的類有資格獲得Spring轉換的目的。這個注解也是@Component 的一個特殊實現,允許實現類能夠被自動掃描到

  • @Component: 表明這個注釋的類是一個組件,當使用基於注釋的配置和類路徑掃描時,這些類被視為自動檢測的候選者。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component public @interface Controller {

    @AliasFor(annotation = Component.class)
    String value() default "";

}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component public @interface Service {

    @AliasFor(annotation = Component.class)
    String value() default "";

}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component public @interface Repository {

    @AliasFor(annotation = Component.class)
    String value() default "";

}

我們可以看到@Controller, @Service, @Repository這三個注解上都有@Component這個注解

也就是說,上面四個注解標記的類都能夠通過@ComponentScan 掃描到,上面四個注解最大的區別就是使用的場景和語義不一樣,比如你定義一個Service類想要被Spring進行管理,你應該把它定義為@Service 而不是@Controller因為我們從語義上講,@Service更像是一個服務的類,而不是一個控制器的類,@Component通常被稱作組件,它可以標注任何你沒有嚴格予以說明的類,比如說是一個配置類,它不屬於MVC模式的任何一層,這個時候你更習慣於把它定義為 @Component。@Controller,@Service,@Repository 的注解上都有@Component,所以這三個注解都可以用@Component進行替換。

同@Import注解組合使用

新建一個配置類,例如數據庫配置類:

@Configuration
public class DatabaseConfig {
    @Bean
    public DataSource dataSource(){
        return new DataSource(){
            ...
        };
    }
}

然后在AppConfig中用@Import來導入配置類

@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
    @Autowired
    public DataSource dataSource; //注入的bean在DatabaseConfig.class中定義
    @Bean
    IBean appBean(){
        return new AppBean();
    }
}

最后執行:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
DatabaseConfig dataSourceConfig = context.getBean(DatabaseConfig.class);

可以看到只注冊了AppConfig.class,容器自動會把@Import指向的配置類初始化。

同@Profile注解組合使用

在配置類中可以申明@Profile注解,僅當滿足profile條件時,才會處理配置類,也可以將@Profile注解加載配置類中的每一個@Bean來實現更細粒度的條件控制。

@Configuration
@Profile("develop")
public class DatabaseConfig {
    @Bean
    public DataSource dataSource(){
        return new DataSource(){...};
    }
}

嵌套使用@Configuration

在配置類中可以創建靜態內部類,並添加@Configuration注解,這樣上下文只需要注冊最外面的配置類,內部的配置類會自動被加載。這樣做省略了@Import,因為本身就在配置類內部,無需再特別指定了。

@Configuration public class AppConfig {
    @Autowired
    public DataSource dataSource; //注入的bean在內部定義
 @Configuration public static class DatabaseConfig{
        @Bean
        DataSource dataSource(){
            return new DataSource() {...};
        }
    }
    
    @Bean
    IBean appBean(){
        return new AppBean();
    }
}

注意:任何嵌套的@Configuration 都必須是static 的。

@Lazy初始化

默認情況下,配置類中的Bean都隨着應用上下文被初始化,可以在配置類中添加@Lazy注解來延遲初始化,當然也可以在每個@Bean注解上添加,來實現更細粒度的控制。

@Configuration
@Lazy//延時加載
public class AppConfig {
    @Bean
    IBean appBean(){
        return new AppBean();
    }
}

配置類約束

  • 配置類必須為顯式申明的類,而不能通過工廠類方法返回實例。允許運行時類增強。
  • 配置類不允許標記final。
  • 配置類必須全局可見(不允許定義在方法本地內部類中)
  • 嵌套配置類必須申明為static 內部類
  • @Bean方法不可以再創建新的配置類(所有實例都當做bean處理,不解析相關配置注解)

@Configuration源碼

ApplicationContext的refresh方法

在我之前的一篇文章spring5 源碼深度解析-----ApplicationContext容器refresh過程中寫過,Spring容器啟動時,即ApplicationContext接口實現類的對象實例執行refresh方法時,在Bean初始化完成之前,有一個擴展點,用來操作BeanFactory,來擴展對應的功能,比喻往BeanFactory中注冊BeanDefintion,我們回顧一下ApplicationContext的refresh函數:

 1 public void refresh() throws BeansException, IllegalStateException {
 2     synchronized (this.startupShutdownMonitor) {
 3         //准備刷新的上下文 環境  
 4         prepareRefresh();
 5         //初始化BeanFactory,並進行XML文件讀取  
 6         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 7         //對beanFactory進行各種功能填充  
 8         prepareBeanFactory(beanFactory);
 9         try {
10             postProcessBeanFactory(beanFactory);
11             //激活各種beanFactory處理器  
12  invokeBeanFactoryPostProcessors(beanFactory); 13             //注冊攔截Bean創建的Bean處理器,這里只是注冊,真正的調用實在getBean時候 
14             registerBeanPostProcessors(beanFactory);
15             //為上下文初始化Message源,即不同語言的消息體,國際化處理  
16             initMessageSource();
17             //初始化應用消息廣播器,並放入“applicationEventMulticaster”bean中  
18             initApplicationEventMulticaster();
19             //留給子類來初始化其它的Bean  
20             onRefresh();
21             //在所有注冊的bean中查找Listener bean,注冊到消息廣播器中  
22             registerListeners();
23             //初始化剩下的單實例(非惰性的)  
24             finishBeanFactoryInitialization(beanFactory);
25             //完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程,同時發出ContextRefreshEvent通知別人  
26             finishRefresh();
27         }
28         catch (BeansException ex) {
29             if (logger.isWarnEnabled()) {
30                 logger.warn("Exception encountered during context initialization - " +
31                         "cancelling refresh attempt: " + ex);
32             }
33             destroyBeans();
34             cancelRefresh(ex);
35             throw ex;
36         }
37         finally {
38             resetCommonCaches();
39         }
40     }
41 }

看到第12行,在初始化BeanFactory后,會激活各種beanFactory處理器,我們來看看invokeBeanFactoryPostProcessors方法

 1 public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
 2 
 3     // Invoke BeanDefinitionRegistryPostProcessors first, if any.
 4     // 1、首先調用BeanDefinitionRegistryPostProcessors
 5     Set<String> processedBeans = new HashSet<>();
 6 
 7     // beanFactory是BeanDefinitionRegistry類型
 8     if (beanFactory instanceof BeanDefinitionRegistry) {
 9         BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
10         // 定義BeanFactoryPostProcessor
11         List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
12         // 定義BeanDefinitionRegistryPostProcessor集合
13         List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
14 
15         // 循環手動注冊的beanFactoryPostProcessors
16         for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
17             // 如果是BeanDefinitionRegistryPostProcessor的實例話,則調用其postProcessBeanDefinitionRegistry方法,對bean進行注冊操作
18             if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
19                 // 如果是BeanDefinitionRegistryPostProcessor類型,則直接調用其postProcessBeanDefinitionRegistry
20                 BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
21  registryProcessor.postProcessBeanDefinitionRegistry(registry); 22                 registryProcessors.add(registryProcessor);
23             }
24             // 否則則將其當做普通的BeanFactoryPostProcessor處理,直接加入regularPostProcessors集合,以備后續處理
25             else {
26                 regularPostProcessors.add(postProcessor);
27             }
28         }
29         //略....
30     }
31 
32     // 2、如果不是BeanDefinitionRegistry的實例,那么直接調用其回調函數即可-->postProcessBeanFactory
33     else {
34         // Invoke factory processors registered with the context instance.
35         invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
36     }
37     //略....
38 }

我們看看第21行,看看其實現類,如下截圖,發現其中有一個ConfigurationClassPostProcessor,這個類就是本章的重點

ConfigurationClassPostProcessor這個BeanFactoryPostProcessor,來開啟整個@Configuration注解的系列類的加載的,即開啟基於@Configuration的類配置代替beans標簽的容器配置的相關bean的加載。

ConfigurationClassPostProcessor

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//生成唯一標識,用於重復處理驗證
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);
   //解析Java類配置bean
 processConfigBeanDefinitions(registry);
}

processConfigBeanDefinitions(registry)處理邏輯:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
  //所有已經注冊的bean 
   String[] candidateNames = registry.getBeanDefinitionNames(); //遍歷bean定義信息
   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
         }
      }
    //1.如果當前的bean是Javabean配置類(含有@Configuration注解的類),則加入到集合configCandidates中
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
         configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
   }

   // Return immediately if no @Configuration classes were found
  // 沒有@Configuration注解的類,直接退出
   if (configCandidates.isEmpty()) {
      return;
   }

   // 多個Java配置類,按@Ordered注解排序
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });

   // Detect any custom bean name generation strategy supplied through the enclosing application context
   SingletonBeanRegistry sbr = null;
   if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
         if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
         }
      }
   }

   if (this.environment == null) {
      this.environment = new StandardEnvironment();
   }

   // Parse each @Configuration class
  //初始化一個ConfigurationClassParser解析器,可以解析@Congiguration配置類
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
   //2.解析Java配置類
 parser.parse(candidates);
   //主要校驗配置類不能使用final修飾符(CGLIB代理是生成一個子類,因此原先的類不能使用final修飾)
      parser.validate();

      //排除已處理過的配置類
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);
       
      // Read the model and create bean definitions based on its content
      if (this.reader == null) {
         this.reader = new ConfigurationClassBeanDefinitionReader(
               registry, this.sourceExtractor, this.resourceLoader, this.environment,
               this.importBeanNameGenerator, parser.getImportRegistry());
      }
    //3.加載bean定義信息,主要實現將@bean @Configuration @Import @ImportResource @ImportRegistrar注冊為bean
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);
      //清空已處理的配置類
      candidates.clear();
   //再次獲取容器中bean定義數量  如果大於 之前獲取的bean定義數量,則說明有新的bean注冊到容器中,需要再次解析
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
         String[] newCandidateNames = registry.getBeanDefinitionNames();
         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
         Set<String> alreadyParsedClasses = new HashSet<>();
         for (ConfigurationClass configurationClass : alreadyParsed) {
            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
         }
         for (String candidateName : newCandidateNames) {
            if (!oldCandidateNames.contains(candidateName)) {
               BeanDefinition bd = registry.getBeanDefinition(candidateName);
        //新注冊的bean如果也是@Configuration配置類,則添加到數據,等待解析
               if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                     !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                  candidates.add(new BeanDefinitionHolder(bd, candidateName));
               }
            }
         }
         candidateNames = newCandidateNames;
      }
   }
   while (!candidates.isEmpty());

   // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
   if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
   }

   if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
      // for a shared cache since it'll be cleared by the ApplicationContext.
      ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
   }
}

processConfigBeanDefinitions整個方法可以大體划分為三個階段:

  1. 從容器中獲取和Configuration有關系的BeanDefinition
  2. 以該BeanDefinition為起點,進行解析操作,得到解析結果集
  3. 將解析到的結果集加載到容器中,即構造成一個BeanDefinition放到容器中待初始化

1、判斷類是否與@Configuration有關

在上面第1步中,有@Configuration注解的會加入到集合當中,這個判斷是在ConfigurationClassUtils#checkConfigurationClassCandidate當中實現

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }
    //獲取注解元數據信息
    AnnotationMetadata metadata;
    if (beanDef instanceof AnnotatedBeanDefinition &&
            className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
        metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
    }
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
        Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
        metadata = new StandardAnnotationMetadata(beanClass, true);
    }
    else {
        try {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
            metadata = metadataReader.getAnnotationMetadata();
        }
        catch (IOException ex) {
            return false;
        }
    }
    // 查找當前注解是否是與@Configuration相關
    // 該方法還會判斷該注解上的注解是否有@Configuration,一直往上尋找
    // 因為有的注解為復合注解
    if (isFullConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 查找當前注解上是否有ComponentScan、Component、Import、ImportResource注解
    //如果沒有則查找Bean注解,同上,一直往上查找
    else if (isLiteConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }
    return true;
}

我們看看isFullConfigurationCandidate和isLiteConfigurationCandidate

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
}
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
        return false;
    }

    // Any of the typical annotations found?
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }

    // Finally, let's look for @Bean methods...
    try {
        return metadata.hasAnnotatedMethods(Bean.class.getName());
    }
    catch (Throwable ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
        }
        return false;
    }
}

private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

2、解析Java配置類parser.parse(candidates)

parser.parse(candidates)方法最終調用processConfigurationClass方法來處理@Configuration配置類,ConfigurationClassParser. processConfigurationClass()方法實現代碼如下:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
  //判斷是否需要解析
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }
   //判斷同一個配置類是否重復加載過,如果重復加載過,則合並,否則從集合中移除舊的配置類,后續邏輯將處理新的配置類
   ConfigurationClass existingClass = this.configurationClasses.get(configClass);
   if (existingClass != null) {
      if (configClass.isImported()) {
         if (existingClass.isImported()) {
            existingClass.mergeImportedBy(configClass);
         }
         // Otherwise ignore new imported config class; existing non-imported class overrides it.
         return;
      }
      else {
         // Explicit bean definition found, probably replacing an import.
         // Let's remove the old one and go with the new one.
         this.configurationClasses.remove(configClass);
         this.knownSuperclasses.values().removeIf(configClass::equals);
      }
   }

   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass);
   do {
     //【真正解析配置類】
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
   }
   while (sourceClass != null);
   //再次添加到到集合中
   this.configurationClasses.put(configClass, configClass);
}

doProcessConfigurationClass方法主要實現從配置類中解析所有bean,包括處理內部類,父類以及各種注解

ConfigurationClassParser. doProcessConfigurationClass()解析配置類邏輯如下:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {

   //遞歸處理任何成員(嵌套)類
   processMemberClasses(configClass, sourceClass);

   // 處理@PropertySource注解
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // 處理@ComponentScan //獲取@ComponentScan注解信息
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {

         // 按@CmponentScan注解掃描bean
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 遍歷掃描出的bean定義是否是配置類bean
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            //若果掃描出的bean定義是配置類(含有@COnfiguration),則繼續調用parse方法,內部再次調用doProcessConfigurationClas(),遞歸解析
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   //處理@Import注解
   processImports(configClass, sourceClass, getImports(sourceClass), true);

   //處理@ImportResource注解
   AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   //處理@Bean注解 
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      //將解析出的所有@Bean注解方法添加到configClass配置類信息中
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   //處理接口中所有添加@Bean注解的方法,內部通過遍歷所有接口,解析得到@Bean注解方法,並添加到configClass配置類信息中
   processInterfaces(configClass, sourceClass);

   // 如果有父類,則返回父類,遞歸執行doProcessConfigurationClass()解析父類
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

下面看兩個很重要的注解@Bean和@ComponentScan的實現過程

  • @ComponentScan注解解析過程

Set<BeanDefinitionHolder> scannedBeanDefinitions =  this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

@ComponentScan注解解析,從上面的代碼可以看出@ComponentScan注解解析通過調用ComponentScanAnnotationParser的parse方法完成,而parse()方法內部處理了一些scanner屬性(過濾器設置)和basePackages包名處理,最終通過調用ClassPathBeanDefinitionScanner.doScan方法實現掃面工作

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
   ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
         componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

   Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
   boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
   scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
         BeanUtils.instantiateClass(generatorClass));

   ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
   if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
      scanner.setScopedProxyMode(scopedProxyMode);
   }
   else {
      Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
      scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
   }

   scanner.setResourcePattern(componentScan.getString("resourcePattern"));

   for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addIncludeFilter(typeFilter);
      }
   }
   for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addExcludeFilter(typeFilter);
      }
   }

   boolean lazyInit = componentScan.getBoolean("lazyInit");
   if (lazyInit) {
      scanner.getBeanDefinitionDefaults().setLazyInit(true);
   }

   Set<String> basePackages = new LinkedHashSet<>();
   String[] basePackagesArray = componentScan.getStringArray("basePackages");
   for (String pkg : basePackagesArray) {
      String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      Collections.addAll(basePackages, tokenized);
   }
   for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
   }
   if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(declaringClass));
   }

   scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
         return declaringClass.equals(className);
      }
   });
   return scanner.doScan(StringUtils.toStringArray(basePackages));
}

doScan掃描basePackages下所有bean

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
    //根據basePackage加載包下所有java文件,並掃描出所有bean組件    
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    //遍歷beandefition
      for (BeanDefinition candidate : candidates) {
      //解析作用域Scope
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
      //通用注解解析到candidate結構中,主要是處理Lazy, primary DependsOn, Role ,Description這五個注解
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
      //檢查當前bean是否已經注冊,不存在則注冊
         if (checkCandidate(beanName, candidate)) {
           BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
        // 注冊到ioc容器中,主要是一些@Component組件,@Bean注解方法並沒有在此處注冊,beanname和beandefinition 鍵值對
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

ClassPathBeanDefinitionScanner.scanCandidateComponents實現bean定義信息掃描

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
   // @ComponentScan("com.sl.springlearning.extension")包路徑處理:packageSearchPath = classpath*:com/sl/springlearning/extension/**/*.class
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
   //獲取當前包下所有的class文件
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
          //按照scanner過濾器過濾,比如配置類本身將被過濾掉,沒有@Component等組件注解的類將過濾掉
               //包含@Component注解的組件將創建BeanDefinition
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               } else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}
  • @Bean注解解析過程

retrieveBeanMethodMetadata方法實現了@Bean方法的解析,但並未將實現bean實例的創建。

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
   AnnotationMetadata original = sourceClass.getMetadata();
    //獲取所有@Bean注解的方法
   Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName()); // 如果配置類中有多個@Bean注解的方法,則排序
   if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
      // Try reading the class file via ASM for deterministic declaration order...
      // Unfortunately, the JVM's standard reflection returns methods in arbitrary
      // order, even between different runs of the same application on the same JVM.
      try {
         AnnotationMetadata asm =
               this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
         Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
         if (asmMethods.size() >= beanMethods.size()) {
            Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
            for (MethodMetadata asmMethod : asmMethods) {
               for (MethodMetadata beanMethod : beanMethods) {
                  if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                     selectedMethods.add(beanMethod);
                     break;
                  }
               }
            }
            if (selectedMethods.size() == beanMethods.size()) {
               // All reflection-detected methods found in ASM method set -> proceed
               beanMethods = selectedMethods;
            }
         }
      }
      catch (IOException ex) {
         logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
         // No worries, let's continue with the reflection metadata we started with...
      }
   }
   return beanMethods;
}

3.加載bean定義信息  this.reader.loadBeanDefinitions(configClasses)

  回到ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,當調用完parse方法之后,能得到一批ConfigurationClass集合,但是這時候只是獲取到,而容器中還沒有對應的注冊信息,那么接下來就是對這批集合進行注冊處理

  ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法的功能就是將之前解析出的configClasses配置類信息中所有配置相關的信息添加到spring的bean定義,主要是配置類中的@Bean注解方法,配置類@ImportResource和@Import(實現ImportBeanDefinitionRegistrar接口方式)的bean注冊

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法 實現邏輯如下:

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}
private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
     if (trackedConditionEvaluator.shouldSkip(configClass)) {
      String beanName = configClass.getBeanName();
      if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
         this.registry.removeBeanDefinition(beanName);
      }
      this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
      return;
   }
    //與@Import注解相關
   if (configClass.isImported()) {
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
  //將@Bean方法注冊為bean
   for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //將configClass中中ImportResource指定的資源注冊為bean
   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    //將configClass中ImportedRegistrar注冊為bean
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

主要看下loadBeanDefinitionsForBeanMethod方法

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    //獲取方法名
    String methodName = metadata.getMethodName(); // Do we need to mark the bean as skipped by its condition?
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
        configClass.skippedBeanMethods.add(methodName);
        return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
        return;
    }

    //獲取@Bean注解的元數據信息
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");

    // Consider name and any aliases
    //獲取@Bean注解是否有name屬性,如@Bean(name = "myBean")
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    //默認bean的名稱和方法名稱相同,但是如果設置了name,就取name作為beanName
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    //創建一個BeanMethod的BeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setResource(configClass.getResource());
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

    //設置工廠方法 //后期Bean的實例化,getBean的時候,會判斷BeanMethod是否存在FactoryMethod,如果存在,就使用反射調用工廠方法,返回工廠方法中的對象
    if (metadata.isStatic()) { // static @Bean method
 beanDef.setBeanClassName(configClass.getMetadata().getClassName()); beanDef.setFactoryMethodName(methodName); } else { // instance @Bean method
 beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(methodName); } //....
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

上面只列出了核心代碼,主要是構造了BeanDefinition,然后注冊進容器,而BeanDefinition的一些屬性則是由注解中獲取,這部分代碼省略。

另外,可以看到@Bean的方式構造的BeanDefinition的時候,與普通的不同,這種方式是會設置工廠方法去初始化,也就是說,AppConfig 類下的appBean方法被Spring當成一個工廠方法,也就是說這種方式與下列的初始化方式原理類似:

<bean id="appConfig" class="com.example.springboot.springbootdemo.bean.AppConfig"/>

<bean id="appBean" factory-bean="appConfig" factory-method="appBean"></bean>

總結

處理邏輯理了一遍后,看一下ConfigurationClassPostProcessor處理器解析@configuration配置類主要過程:

  1. Spring容器初始化時注冊默認后置處理器ConfigurationClassPostProcessor

  2. Spring容器初始化執行refresh()方法中調用ConfigurationClassPostProcessor

  3. ConfigurationClassPostProcessor處理器借助ConfigurationClassParser完成配置類解析

  4. ConfigurationClassParser配置內解析過程中完成嵌套的MemberClass、@PropertySource注解、@ComponentScan注解(掃描package下的所有Class並進行迭代解析,主要是@Component組件解析及注冊)、@ImportResource、@Bean等處理

  5. 完成@Bean注冊, @ImportResource指定bean的注冊以及@Import(實現ImportBeanDefinitionRegistrar接口方式)的bean注冊

  6.有@Bean注解的方法在解析的時候作為ConfigurationClass的一個屬性,最后還是會轉換成BeanDefinition進行處理, 而實例化的時候會作為一個工廠方法進行Bean的創建

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM