ConfigurationClassPostProcessor
前面说到ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor接口的实现,在调用invokeBeanFactoryPostProcessors时,会先被调用;而且通过之前的分析我们可以知道,它是在容器初始化置AnnotatedBeanDefinitionReader时注册到注册的(分析(二)),主要用来处理@Configuration注解。因实现还是比较复杂的,所以单独拿出来分析下。
先看一下该类实现的接口
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { ....}复制代码
它除了是BeanDefinitionRegistryPostProcessor的实现之外,还实现了一堆的Aware接口。我们在前面有介绍ApplicationContextAwareProcessor这个bean的扩展实现的功能,注入上下文属性(在调用getBean进行实例化时调用的,关于getBean调用过程后面再介绍)。
1. postProcessBeanDefinitionRegistry
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); processConfigBeanDefinitions(registry); }复制代码
代码比较简单,取得registry的id并做判重处理或记录,直接看processConfigBeanDefinitions方法,代码如下.
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { ListconfigCandidates = new ArrayList<>(); // 获取容器中注册的所有bean名字 String[] candidateNames = registry.getBeanDefinitionNames(); //遍历bean for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); //判断bean是否已经处理过 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 判断是否是Configuration类, 这里其实主要是看是否有 // @Configuration, @Component, @ComponentScan, @Import, @Bean等注解 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 这里筛选完之后,符合条件的只剩启动类 @SpringBootApplication if (configCandidates.isEmpty()) { return; } // 排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // 设置beanName的生成策略 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(); } // 配置@Configuration的解析器,这里的部分属性就是通过ApplicationContextAwareProcessor来设置注入的。 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); // 解析 do { parser.parse(candidates); parser.validate(); Set 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()); } // 读取配置类,加载beanDefinition this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); // 如果在解析过程中,有新的配置类型beanDefinition注册加入,则循环处理 if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set 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); 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(); } }复制代码
- 获取已经注册的bean名称,进行遍历; 判断是否已经处理过,判断是否为配置类;如果有,排下序。
- 如果BeanDefinitionRegistry 是SingletonBeanRegistry 子类的话,由于我们当前传入的是DefaultListableBeanFactory,是 SingletonBeanRegistry 的子类。因此会将registry强转为SingletonBeanRegistry;然后获取beanName的生成策略设置给成员变量。
- 实例化ConfigurationClassParser 为了解析各个配置类.实例化2个set,candidates 用于将之前加入的configCandidates 进行去重,alreadyParsed 用于判断是否处理过。
- 调用ConfigurationClassParser#parse进行解析 将解析过的配置类加入到configClasses;实例化ConfigurationClassBeanDefinitionReader 调用ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 进行加载,并加入到alreadyParsed中,用于去重 将candidates进行清空,如果registry中注册的bean的数量 大于 之前获得的数量,则意味着在解析过程中又新加入了很多,那么就需要对其进行解析
- 注册一个,bean 为 ImportRegistry;清除缓存。
这里有一个判断是否为配置类的方法,其实检查的就是该类是否有@Configuration,@Component,@ComponentScan, @Import, @ImportResource, @Bean方法。
2. ConfigurationClassParser.parse
该方法的代码简单,最后调用会到processConfigurationClass方法,我们直接看该方法实现。
processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { //是否跳过 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); // 处理Imported 的情况 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. 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);}复制代码
- 先看ConditionEvaluator.shouldSkip判断是否跳过,根据@Condition条件来判断。
- 判断处理imported的情况
- 递归解析configClass
3. doProcessConfigurationClass
解析class配置的核心方法,代码如下:
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 类 SetcomponentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // 如果配置类有@ComponentScan -> 立刻运行扫描 Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 检查扫描到的definition是否还有配置类型,有则递归解析 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } 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 readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // 处理 @Bean 方法注解 Set beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 处理接口的默认方法 processInterfaces(configClass, sourceClass); // 处理父类 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;}复制代码
3.1 处理内部类,不再贴代码;
- 遍历configClass的内部类,判断是否为配置类且类名不与configClass相同
- 判断是否存在循环引用
3.2 处理@PropertySource.通过遍历该类中的@PropertySource的注解,如果该类中的environment是ConfigurableEnvironment 子类的话,则调用processPropertySource进行处理.否则打印警告日志.一般都是ConfigurableEnvironment的子类
private void processPropertySource(AnnotationAttributes propertySource) throws IOException { //获取注解上的属性name String name = propertySource.getString("name"); if (!StringUtils.hasLength(name)) { name = null; } //获取注解上的属性encoding String encoding = propertySource.getString("encoding"); if (!StringUtils.hasLength(encoding)) { encoding = null; } String[] locations = propertySource.getStringArray("value"); Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required"); boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound"); // 获取Facotry,如果没指定使用默认的DefaultPropertySourceFactory Class factoryClass = propertySource.getClass("factory"); PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); for (String location : locations) { try { // 解析点位符,多环境配置 String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); // 加载property配置文件 Resource resource = this.resourceLoader.getResource(resolvedLocation); addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) { // Placeholders not resolvable or resource not found when trying to open it if (ignoreResourceNotFound) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } }}复制代码
解析@PropertySource注解上的各属性值,加载配置文件,然后添加到运行环境中。
3.3 处理@ComponentScan:判断是否应该跳过,并依次通过ComponentScanAnnotationParser.parse进行解析。最终调用ClassPathBeanDefinitionScanner.doScan。代码如下:
protected SetdoScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 扫描basePackage路径下的java文件 Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 解析是否有@Scop ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 生成beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 设置默认配置 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 通用注解的解析设置 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 判断beanName容器中是否存在,不存在则注册,如存在判断是否兼容,不兼容抛异常 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }复制代码
那么,通过上面一步后,我们自己写的@Controller, @Service等就被注册到容器中了。只是完成了beanDefinition的查找,解析并注册的工作,此时这些bean还未被实例化。ok, 回到doProcessConfigurationClass方法中对@ComponentScan的解析处接着看,因为@Controller, @Service, @Configuration等注解都是符合配置类校验的(ConfigurationClassUtils.checkConfigurationClassCandidate),所以还需要递归解析来看其他内部是否还有其他配置,比如内部类,@Bean等。
接着再往下,以次解析的是@Import, @ImportResource, 收集@Bean方法与接口的默认方法。这部分不再一一分析,流程和上面类似递归处理,最后把configClass都放到了集合中。
解析处理完之后,回到我们的ConfigurationClassPostProcessor方法继续看,实例化了reader类ConfigurationClassBeanDefinitionReader用来读取加载configClass的解析。
public void loadBeanDefinitions(SetconfigurationModel) { 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 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 注册@ImportResource 通过XmlBeanDefinitionReader读取配置 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 注册ImportBeanDefinitionRegistrar实现 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}复制代码
通过之前的分析,其实我们知道@PropertySource,@ComponentScan的解析最后会把相关注解增加到环境配置或者注册到容器中去。但对@Import, @Bean的只是收集到集合中,并未注册到容器中,所以上面的load主要处理这一部分。
- registerBeanDefinitionForImportedConfigurationClass 注册@Import
- loadBeanDefinitionsForBeanMethod 注册@Bean
- loadBeanDefinitionsFromImportedResources 注册@ImportResource 通过XmlBeanDefinitionReader读取配置
- loadBeanDefinitionsFromRegistrars 注册ImportBeanDefinitionRegistrar实现
processConfigBeanDefinitions方法再下面的处理就是判断新增的beanDefinition是否有配置类,有则递归处理,无则清除缓存。
该篇文章分析详细,建议阅读一下。
参考链接: