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 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 candidates = new LinkedHashSet<>(configCandidates);
57 Set 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 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 oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
83 Set 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 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 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 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 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 scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
@ComponentScan注解解析,从上面的代码可以看出@ComponentScan注解解析通过调用ComponentScanAnnotationParser的parse方法完成,而parse()方法内部处理了一些scanner属性(过滤器设置)和basePackages包名处理,最终通过调用ClassPathBeanDefinitionScanner.doScan方法实现扫面工作

View Code
doScan扫描basePackages下所有bean

复制代码
1 protected Set doScan(String… basePackages) {
2 Assert.notEmpty(basePackages, “At least one base package must be specified”);
3 Set beanDefinitions = new LinkedHashSet<>();
4 for (String basePackage : basePackages) {
5     //根据basePackage加载包下所有java文件,并扫描出所有bean组件, findCandidateComponents方法内部调用ClassPathScanningCandidateComponentProvider.scanCandidateComponents(backPackages)
6 Set 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 scanCandidateComponents(String basePackage) {
2 Set 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 retrieveBeanMethodMetadata(SourceClass sourceClass) {
2 AnnotationMetadata original = sourceClass.getMetadata();
3 //获取所有@Bean注解的方法
4 Set 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 asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
14 if (asmMethods.size() >= beanMethods.size()) {
15 Set 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 if (selectedMethods.size() == beanMethods.size()) {
25 // All reflection-detected methods found in ASM method set -> proceed
26 beanMethods = selectedMethods;
27 }
28 }
29 }
30 catch (IOException ex) {
31 logger.debug(“Failed to read class file via ASM for determining @Bean method order”, ex);
32 // No worries, let’s continue with the reflection metadata we started with…
33 }
34 }
35 return beanMethods;
36 }
复制代码
2.加载bean定义信息 this.reader.loadBeanDefinitions(configClasses)

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法的功能就是将之前解析出的configClasses配置类信息中所有配置相关的信息添加到spring的bean定义,主要是配置类中的@Bean注解方法,配置类@ImportResource和@Import(实现ImportBeanDefinitionRegistrar接口方式)的bean注册

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法 实现逻辑如下:

复制代码
1 public void loadBeanDefinitions(Set configurationModel) {
2 TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
3 for (ConfigurationClass configClass : configurationModel) {
4 loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
5 }
6 }
复制代码
复制代码
1 private void loadBeanDefinitionsForConfigurationClass(
2 ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
3 if (trackedConditionEvaluator.shouldSkip(configClass)) {
4 String beanName = configClass.getBeanName();
5 if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
6 this.registry.removeBeanDefinition(beanName);
7 }
8 this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
9 return;
10 }
11
12 if (configClass.isImported()) {
13 registerBeanDefinitionForImportedConfigurationClass(configClass);
14 }
15   //将@Bean方法注册为bean
16 for (BeanMethod beanMethod : configClass.getBeanMethods()) {
17 loadBeanDefinitionsForBeanMethod(beanMethod);
18 }
19 //将configClass中中ImportResource指定的资源注册为bean
20 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
21 //将configClass中ImportedRegistrar注册为bean
22 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
23 }
复制代码
处理逻辑理了一遍后,看一下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注册

你可能感兴趣的:(Spring源码解析,–,Java基础,Spring源码解析,–)