本文分析最重要的一个PostProcessor,如果还不了解PostProcessor,请先阅读上篇文章,ConfigurationClassPostProcessor完成了对配置类的解析,我们我们通常通过ApplicationContext的构造方法或者ApplicationContext#register方法注册到容器中,那么spring容器时如何判断你传入的类是不是配置类呢?判断为配置类之后又是如何处理的呢?
所以分析之前我们提出两个问题:
1、spring会把什么样的类判断为配置类?
2、ConfigurationClassPostProcessor关键步骤有哪些?
分析的过程我们就围绕这两个问题
ConfigurationClassPostProcessor源码解析
ConfigurationClassPostProcessor继承了BeanDefinitionRegistryPostProcessor类,继承这个类就要实现下面这两个方法,这两个方法主要是执行时机不同,具体的执行时机可以参考上一篇文章。那么我们就对这两个方法分别进行分析。
postProcessBeanDefinitionRegistry方法解析
这里直接交给processConfigBeanDefinitions(registry)执行
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 存储configuration类用的
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 能获取到5个spring内置的和自己register的
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 取BD里面的ConfigurationClassPostProcessorConfigurationClass属性 刚进来的时候为空
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
// 已经被标识为配置类了,当前就不需要处理了
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 检查是否为配置类
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
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
// 如果单例池里有BeanNameGenerator,后面的扫描就使用单例池里的BeanNameGenerator
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.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 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 {
// 解析出配置类衍生出来的类
parser.parse(candidates);
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());
}
// 加载进BDMap 这里主配置类已经在BDMap存在,会删了再添加
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 判断有没有新进来的bd,新进来的递归执行
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);
// 打标签是全配置还是半配置类
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();
}
}
这个方法代码很长,下面我们只分析关键流程,分支流程不影响我们阅读源码
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
// 已经被标识为配置类了,当前就不需要处理了
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 检查是否为配置类
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
这里ConfigurationClassUtils.checkConfigurationClassCandidate对配置类进行判断,如果没有配置类,直接就return了,不在执行下面的代码,我们先看对配置类的判断逻辑
这里只看checkConfigurationClassCandidate的关键逻辑
// 获取@Configuration中的信息
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 这里会判断出是不是为config类 config类分为两类
// 第一类:full 使用@Configuration 并且proxyBeanMethods为true 默认就为true
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 第二类:lite 使用@Configuration 或者 类上有@Component、@ComponentScan、@Import、@ImportResource任一注解 或方法上有@bean注解
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
所以这几行代码把配置类分为两种,并在配置类的beanDefination中打上标签,标注为全配置类还是半配置类
使用@Configuration 并且proxyBeanMethods为true就是全配置类
使用@Configuration 并且proxyBeanMethods为false、有@Component、@ComponentScan、@Import、@ImportResource、方法上有@bean注解,满足其中一个条件就是半配置类。
到此就完成了对配置类的标记,那下面就要对配置类解析了,回到processConfigBeanDefinitions方法
// 所有的配置类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
// 这里递归处理配置类 就是处理配置类中的配置类
do {
// 解析出配置类衍生出来的类
parser.parse(candidates);
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());
}
// 加载进BDMap 这里主配置类已经在BDMap存在,会删了再添加
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 判断有没有新进来的bd,新进来的递归执行
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);
// 打标签是全配置还是半配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
就是这一段do while里面完成了对配置类的解析,至于这里为什么要循环后面在做解释。
配置类的解析,关键在于这一行代码
parser.parse(candidates);
一直点进去
该图概括了这个方法的作用,下面我们看这个方法的代码
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
这里又有一个do while循环,至此,已经有两个循环,这个循环的作用也在后面解释
这里最后一行代码千万不能忽略,他决定了最终都有哪些类会进bdmap
进入doProcessConfigurationClass方法,这个方法完成了具体的解析步骤,接下来详细分析这个方法,分析之前先把我看源码是画的流程图贴在这里
下面是doProcessConfigurationClass的代码
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 递归处理内部类
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
// 处理@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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 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) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 判断ComponentScan扫描进来的类是否为配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 扫描出来的配置类递归处理,如果不是配置类,什么都不做,所以ComponentScan只能扫描进来泛化的配置类
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
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);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
//add进 configClass#beanMethods
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 对接口里面的@Bean进行处理
processInterfaces(configClass, sourceClass);
// Process superclass, if any
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;
}
处理内部类 processMemberClasses
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// 递归处理(如果子类是配置类的话处理子类)
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
这个方法的作用可以概括为一下两点:
如果内部类是包含配置类,把自己放进importStack,并让内部类走一遍配置类处理流程,然后出importStack
如果内部类没有配置类,什么都不做
这里的配置类就是上面的判断标准
处理@PropertySource processPropertySource(propertySource);
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
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");
Class<? extends PropertySourceFactory> 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);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException 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;
}
}
}
}
把properties文件解析并存入environment
处理@ComponentScan
扫描出来的配置类递归处理,如果不是配置类,什么都不做,所以ComponentScan只能扫描进来泛化的配置类
处理@Imports processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 来自循环 && 链式导入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 先把自己进importStack
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归导入,这种被导入的类作为工具,一直到其他两种类型
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// add进 configClass#importBeanDefinitionRegistrars
// loadBeanDefinitions时执行的
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// import 普通类
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// add进 importStack.imports
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 作为配置类递归处理
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
// 最终让自己出栈
this.importStack.pop();
}
}
}
所以这里会把import的类分为三种种类分别处理
ImportSelector 这种被导入的类作为工具,一直到其他两种类型
ImportBeanDefinitionRegistrar add进configClass#importBeanDefinitionRegistrars 最终由loadBeanDefinitions时执行的
普通类 作为配置类递归处理,其importedBy设置为该类
处理@ImportResource
进configClass#importedResources
处理@Bean 和父类中的@Bean
add进 configClass#beanMethods
处理父类
如果有父类会把父类返回,这里就是第二处循环的原因,为了循环处理父类
第二个循环逻辑结束后有一行代码 this.configurationClasses.put(configClass, configClass) 会把解析过的类暂存
一个配置类的解析parser.parse(candidates)已经完成,但是我们没有解析出来的类是如何进入bdmap的,现在回到processConfigBeanDefinitions方法中
这里就完成了注册到bdmap中
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;
}
// importedBy 进来的
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
// 导入@bean进来的
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// ImportedResources进来的,这里用的xml reader进的bdmap(这里可能进来新的bd,也是外层进行循环的原因)
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 这里可能进来新的bd,也是外层进行循环的原因
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
那么解析完成后,都有哪些类进入bd呢?这里只做总结,具体的过程不在分析
我把这些类分成四类:
- importedBy 进来的
- 导入@bean进来的
- ImportedResources
- BeanDefinitionRegistrars
这样就完成了配置类的解析已经相关类的bd创建
我们前面留了一个问题,就是第一个循环干什么用的?其实我在第一次看这段代码的时候非常困惑,认为类在解析的时候,已经对一些新出现的配置类做了递归处理了,那么这里为啥还要进行循环,实在想不明白,知道看到了loadBeanDefinitionsFromRegistrars这段代码,才对这个循环的作用有了理解,因为loadBeanDefinitionsFromRegistrars是开放了beanFactory给用户,用户可以注册bd进容器,所以这里可能会有新进来的bd,这里的bd如果是配置类,就需要第一层循环来解析。所以我们在读源码的时候,有看不懂的代码就先放一放,等读到后面可能就有答案了
postProcessBeanFactory方法解析
postProcessBeanDefinitionRegistry方法已经完成了对配置类的解析,那么postProcessBeanFactory方法完成了什么事情呢?其实这里完成了对全配置类的代理,也就是全配置类是以代理类进入容器的,为什么要代理?下面我们看一个例子,看一下做不做代理的区别
这里就不再对这个方法进行分析,不懂java代理使用的朋友可以找资料看下,只要清除代理的使用,看懂这个方法完全没问题
@Configuration
public class MyConfig {
@Bean
public X x(){
return new X();
}
@Bean
public Y y(){
X x = x();
return new Y();
}
}
这里如果你加了@Configuration,spring认为这是一个全配置类,spring会对这个类做代理,代理的作用就是使得X x = x();这行代码中的 x()直接从容器里取。
这里如果没加@Configuration,spring认为这是一个半配置类,spring不会对这个类做代理,代理的作用就是使得X x = x();这行代码中的 x()会再new一个x