1. 程式人生 > >Spring原始碼解析 – @Configuration配置類及註解Bean的解析

Spring原始碼解析 – @Configuration配置類及註解Bean的解析

  在分析Spring 容器建立過程時,我們知道容器預設會載入一些後置處理器PostPRocessor,以AnnotationConfigApplicationContext為例,在建構函式中初始化reader時,載入預設後置處理器。其中 ConfigurationClassPostProcessor這個後置處理器專門處理帶有@Configuration註解的類,ConfigurationClassPostProcessor後置處理實現了BeanDefinitionRegistryPostProcessor介面和PriorityOrdered介面,所以會在容器初始化refres()方法中執行後置處理器時優先執行,主要負責解析所有@Configuration標籤類,並將Bean定義註冊到容器中。

BeanDefinitionRegistryPostProcessor解析配置類過程:

 1 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
 2 //生成唯一標識,用於重複處理驗證
 3    int registryId = System.identityHashCode(registry);
 4    if (this.registriesPostProcessed.contains(registryId)) {
 5       throw new IllegalStateException(
6 "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); 7 } 8 if (this.factoriesPostProcessed.contains(registryId)) { 9 throw new IllegalStateException( 10 "postProcessBeanFactory already called on this post-processor against " + registry);
11 } 12 this.registriesPostProcessed.add(registryId); 13 //解析Java類配置bean 14 processConfigBeanDefinitions(registry); 15 }
processConfigBeanDefinitions(registry)處理邏輯:
  1 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
  2    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
  3   //所有已經註冊的bean 
  4    String[] candidateNames = registry.getBeanDefinitionNames();
  5    //遍歷bean定義資訊
  6    for (String beanName : candidateNames) {
  7       BeanDefinition beanDef = registry.getBeanDefinition(beanName);
  8       if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
  9             ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
 10          if (logger.isDebugEnabled()) {
 11             logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
 12          }
 13       }
 14   //如果當前的bean是Javabean配置類(含有@Configuration註解的類),則加入到集合configCandidates中,
 15       else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
 16          configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
 17       }
 18    }
 19 
 20    // Return immediately if no @Configuration classes were found
 21   // 沒有@Configuration註解的類,直接退出
 22    if (configCandidates.isEmpty()) {
 23       return;
 24    }
 25 
 26    // 多個Java配置類,按@Ordered註解排序
 27    configCandidates.sort((bd1, bd2) -> {
 28       int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
 29       int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
 30       return Integer.compare(i1, i2);
 31    });
 32 
 33    // Detect any custom bean name generation strategy supplied through the enclosing application context
 34    SingletonBeanRegistry sbr = null;
 35    if (registry instanceof SingletonBeanRegistry) {
 36       sbr = (SingletonBeanRegistry) registry;
 37       if (!this.localBeanNameGeneratorSet) {
 38          BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
 39          if (generator != null) {
 40             this.componentScanBeanNameGenerator = generator;
 41             this.importBeanNameGenerator = generator;
 42          }
 43       }
 44    }
 45 
 46    if (this.environment == null) {
 47       this.environment = new StandardEnvironment();
 48    }
 49 
 50    // Parse each @Configuration class
 51   //初始化一個ConfigurationClassParser解析器,可以解析@Congiguration配置類
 52    ConfigurationClassParser parser = new ConfigurationClassParser(
 53          this.metadataReaderFactory, this.problemReporter, this.environment,
 54          this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 55 
 56    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
 57    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
 58    do {
 59     //1.解析Java配置類
 60       parser.parse(candidates);
 61     //主要校驗配置類不能使用final修飾符(CGLIB代理是生成一個子類,因此原先的類不能使用final修飾)
 62       parser.validate();
 63 
 64       //排除已處理過的配置類
 65       Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
 66       configClasses.removeAll(alreadyParsed);
 67        
 68       // Read the model and create bean definitions based on its content
 69       if (this.reader == null) {
 70          this.reader = new ConfigurationClassBeanDefinitionReader(
 71                registry, this.sourceExtractor, this.resourceLoader, this.environment,
 72                this.importBeanNameGenerator, parser.getImportRegistry());
 73       }
 74     //2.載入bean定義資訊,主要實現將@Configuration @Import @ImportResource @ImportRegistrar註冊為bean
 75       this.reader.loadBeanDefinitions(configClasses);
 76       alreadyParsed.addAll(configClasses);
 77       //清空已處理的配置類
 78       candidates.clear();
 79     //再次獲取容器中bean定義數量  如果大於 之前獲取的bean定義數量,則說明有新的bean註冊到容器中,需要再次解析
 80       if (registry.getBeanDefinitionCount() > candidateNames.length) {
 81          String[] newCandidateNames = registry.getBeanDefinitionNames();
 82          Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
 83          Set<String> alreadyParsedClasses = new HashSet<>();
 84          for (ConfigurationClass configurationClass : alreadyParsed) {
 85             alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
 86          }
 87          for (String candidateName : newCandidateNames) {
 88             if (!oldCandidateNames.contains(candidateName)) {
 89                BeanDefinition bd = registry.getBeanDefinition(candidateName);
 90           //新註冊的bean如果也是@Configuration配置類,則新增到資料,等待解析
 91                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
 92                      !alreadyParsedClasses.contains(bd.getBeanClassName())) {
 93                   candidates.add(new BeanDefinitionHolder(bd, candidateName));
 94                }
 95             }
 96          }
 97          candidateNames = newCandidateNames;
 98       }
 99    }
100    while (!candidates.isEmpty());
101 
102    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
103    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
104       sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
105    }
106 
107    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
108       // Clear cache in externally provided MetadataReaderFactory; this is a no-op
109       // for a shared cache since it'll be cleared by the ApplicationContext.
110       ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
111    }
112 }

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

parser.parse(candidates)方法最終呼叫processConfigurationClass方法來處理@Configuration配置類,ConfigurationClassParser. processConfigurationClass()方法實現程式碼如下:

 1 protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
 2   //判斷是否需要解析
 3    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
 4       return;
 5    }
 6    //判斷同一個配置類是否重複載入過,如果重複載入過,則合併,否則從集合中移除舊的配置類,後續邏輯將處理新的配置類
 7    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
 8    if (existingClass != null) {
 9       if (configClass.isImported()) {
10          if (existingClass.isImported()) {
11             existingClass.mergeImportedBy(configClass);
12          }
13          // Otherwise ignore new imported config class; existing non-imported class overrides it.
14          return;
15       }
16       else {
17          // Explicit bean definition found, probably replacing an import.
18          // Let's remove the old one and go with the new one.
19          this.configurationClasses.remove(configClass);
20          this.knownSuperclasses.values().removeIf(configClass::equals);
21       }
22    }
23 
24    // Recursively process the configuration class and its superclass hierarchy.
25    SourceClass sourceClass = asSourceClass(configClass);
26    do {
27      //【真正解析配置類】
28       sourceClass = doProcessConfigurationClass(configClass, sourceClass);
29    }
30    while (sourceClass != null);
31    //再次新增到到集合中
32    this.configurationClasses.put(configClass, configClass);
33 }

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

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

 1 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
 2       throws IOException {
 3 
 4    //遞迴處理任何成員(巢狀)類
 5    processMemberClasses(configClass, sourceClass);
 6 
 7    // 處理@PropertySource註解
 8    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
 9          sourceClass.getMetadata(), PropertySources.class,
10          org.springframework.context.annotation.PropertySource.class)) {
11       if (this.environment instanceof ConfigurableEnvironment) {
12          processPropertySource(propertySource);
13       }
14       else {
15          logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
16                "]. Reason: Environment must implement ConfigurableEnvironment");
17       }
18    }
19 
20    // 處理@ComponentScan 
21 //獲取@ComponentScan註解資訊
22    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
23          sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
24    if (!componentScans.isEmpty() &&
25          !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
26       for (AnnotationAttributes componentScan : componentScans) {
27 
28          // 按@CmponentScan註解掃描bean
29          Set<BeanDefinitionHolder> scannedBeanDefinitions =
30                this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
31          // 遍歷掃描出的bean定義是否是配置類bean
32          for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
33             BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
34             if (bdCand == null) {
35                bdCand = holder.getBeanDefinition();
36             }
37 //若果掃描出的bean定義是配置類(含有@COnfiguration),則繼續呼叫parse方法,內部再次呼叫doProcessConfigurationClas(),遞迴解析
38             if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
39                parse(bdCand.getBeanClassName(), holder.getBeanName());
40             }
41          }
42       }
43    }
44 
45    //處理@Import註解
46    processImports(configClass, sourceClass, getImports(sourceClass), true);
47 
48    //處理@ImportResource註解
49    AnnotationAttributes importResource =
50          AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
51    if (importResource != null) {
52       String[] resources = importResource.getStringArray("locations");
53       Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
54       for (String resource : resources) {
55          String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
56          configClass.addImportedResource(resolvedResource, readerClass);
57       }
58    }
59 
60    //處理@Bean註解 
61    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
62    for (MethodMetadata methodMetadata : beanMethods) {
63 //將解析出的所有@Bean註解方法新增到configClass配置類資訊中
64       configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
65    }
66 
67    //處理介面中所有新增@Bean註解的方法,內部通過遍歷所有介面,解析得到@Bean註解方法,並新增到configClass配置類資訊中
68    processInterfaces(configClass, sourceClass);
69 
70    // 如果有父類,則返回父類,遞迴執行doProcessConfigurationClass()解析父類
71    if (sourceClass.getMetadata().hasSuperClass()) {
72       String superclass = sourceClass.getMetadata().getSuperClassName();
73       if (superclass != null && !superclass.startsWith("java") &&
74             !this.knownSuperclasses.containsKey(superclass)) {
75          this.knownSuperclasses.put(superclass, configClass);
76          // Superclass found, return its annotation metadata and recurse
77          return sourceClass.getSuperClass();
78       }
79    }
80 
81    // No superclass -> processing is complete
82    return null;
83 }

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

@ComponentScan註解解析過程

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

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

 1 public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
 2    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
 3          componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
 4 
 5    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
 6    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
 7    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
 8          BeanUtils.instantiateClass(generatorClass));
 9 
10    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
11    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
12       scanner.setScopedProxyMode(scopedProxyMode);
13    }
14    else {
15       Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
16       scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
17    }
18 
19    scanner.setResourcePattern(componentScan.getString("resourcePattern"));
20 
21    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
22       for (TypeFilter typeFilter : typeFiltersFor(filter)) {
23          scanner.addIncludeFilter(typeFilter);
24       }
25    }
26    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
27       for (TypeFilter typeFilter : typeFiltersFor(filter)) {
28          scanner.addExcludeFilter(typeFilter);
29       }
30    }
31 
32    boolean lazyInit = componentScan.getBoolean("lazyInit");
33    if (lazyInit) {
34       scanner.getBeanDefinitionDefaults().setLazyInit(true);
35    }
36 
37    Set<String> basePackages = new LinkedHashSet<>();
38    String[] basePackagesArray = componentScan.getStringArray("basePackages");
39    for (String pkg : basePackagesArray) {
40       String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
41             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
42       Collections.addAll(basePackages, tokenized);
43    }
44    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
45       basePackages.add(ClassUtils.getPackageName(clazz));
46    }
47    if (basePackages.isEmpty()) {
48       basePackages.add(ClassUtils.getPackageName(declaringClass));
49    }
50 
51    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
52       @Override
53       protected boolean matchClassName(String className) {
54          return declaringClass.equals(className);
55       }
56    });
57    return scanner.doScan(StringUtils.toStringArray(basePackages));
58 }
View Code

doScan掃描basePackages下所有bean

 1 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
 2    Assert.notEmpty(basePackages, "At least one base package must be specified");
 3    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
 4    for (String basePackage : basePackages) {
 5     //根據basePackage載入包下所有java檔案,並掃描出所有bean元件, findCandidateComponents方法內部呼叫ClassPathScanningCandidateComponentProvider.scanCandidateComponents(backPackages)
 6       Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
 7     //遍歷beandefition
 8       for (BeanDefinition candidate : candidates) {
 9       //解析作用域Scope
10          ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
11          candidate.setScope(scopeMetadata.getScopeName());
12          String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
13 //
14          if (candidate instanceof AbstractBeanDefinition) {
15             postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
16          }
17       //通用註解解析到candidate結構中,主要是處理Lazy, primary DependsOn, Role ,Description這五個註解
18          if (candidate instanceof AnnotatedBeanDefinition) {
19             AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
20          }
21       //檢查當前bean是否已經註冊,不存在則註冊
22          if (checkCandidate(beanName, candidate)) {
23             BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
24             definitionHolder =
25                   AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
26             beanDefinitions.add(definitionHolder);
27         // 註冊到ioc容器中,主要是一些@Component元件,@Bean註解方法並沒有在此處註冊, definitionHolder: beanname和beandefinition 鍵值對
28             registerBeanDefinition(definitionHolder, this.registry);
29          }
30       }
31    }
32    return beanDefinitions;
33 }

ClassPathBeanDefinitionScanner.scanCandidateComponents實現bean定義資訊掃描

 1 private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
 2    Set<BeanDefinition> candidates = new LinkedHashSet<>();
 3    try {
 4     // @ComponentScan("com.sl.springlearning.extension")包路徑處理:packageSearchPath = classpath*:com/sl/springlearning/extension/**/*.class
 5       String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
 6             resolveBasePackage(basePackage) + '/' + this.resourcePattern;
 7     //獲取當前包下所有的class檔案
 8       Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
 9       boolean traceEnabled = logger.isTraceEnabled();
10       boolean debugEnabled = logger.isDebugEnabled();
11       for (Resource resource : resources) {
12          if (traceEnabled) {
13             logger.trace("Scanning " + resource);
14          }
15          if (resource.isReadable()) {
16             try {
17                MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
18           //按照scanner過濾器過濾,比如配置類本身將被果過濾掉,沒有@Component等元件註解的類將過濾掉
19                if (isCandidateComponent(metadataReader)) {
20                   ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
21                   sbd.setResource(resource);
22                   sbd.setSource(resource);
23                   if (isCandidateComponent(sbd)) {
24                      if (debugEnabled) {
25                         logger.debug("Identified candidate component class: " + resource);
26                      }
27                      candidates.add(sbd);
28                   }
29                   else {
30                      if (debugEnabled) {
31                         logger.debug("Ignored because not a concrete top-level class: " + resource);
32                      }
33                   }
34                }
35                else {
36                   if (traceEnabled) {
37                      logger.trace("Ignored because not matching any filter: " + resource);
38                   }
39                }
40             }
41             catch (Throwable ex) {
42                throw new BeanDefinitionStoreException(
43                      "Failed to read candidate component class: " + resource, ex);
44             }
45          }
46          else {
47             if (traceEnabled) {
48                logger.trace("Ignored because not readable: " + resource);
49             }
50          }
51       }
52    }
53    catch (IOException ex) {
54       throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
55    }
56    return candidates;
57 }

@Bean註解解析過程

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

 1 private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
 2    AnnotationMetadata original = sourceClass.getMetadata();
 3 //獲取所有@Bean註解的方法
 4    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
 5 // 如果配置類中有多個@Bean註解的方法,則排序
 6    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
 7       // Try reading the class file via ASM for deterministic declaration order...
 8       // Unfortunately, the JVM's standard reflection returns methods in arbitrary
 9       // order, even between different runs of the same application on the same JVM.
10       try {
11          AnnotationMetadata asm =
12                this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
13          Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
14          if (asmMethods.size() >= beanMethods.size()) {
15             Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
16             for (MethodMetadata asmMethod : asmMethods) {
17                for (MethodMetadata beanMethod : beanMethods) {
18                   if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
19                      selectedMethods.add(beanMethod);
20                      break;
21                   }
22                }
23             }
24