ConfigurationClassPostProcessor概述
当XML配置文件中配置了< context:annotation-config/>或者< context:component-scan/>标签时,在解析这两个标签的过程中会注册一个ConfigurationClassPostProcessor。或者,我们当然也可以手动注册一个ConfigurationClassPostProcessor的bean定义,Spring Boot则自动帮我们注册了一个ConfigurationClassPostProcessor。
在AnnotationConfigUtils的registerAnnotationConfigProcessors方法中(该方法在解析< context:annotation-config/>或者< context:component-scan/>标签时被调用),可以看到,如果此前没有手动注册名为CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME=“org.springframework.context.annotation.internalConfigurationAnnotationProcessor”的bean定义,那么以该字符串作为beanName注册一个ConfigurationClassPostProcessor类型的bean定义。
ConfigurationClassPostProcessor的uml类图:
可以看到,ConfigurationClassPostProcessor实现了BeanClassLoaderAware、ResourceLoaderAware、EnvironmentAware的这几个Aware感知接口,那么在创建该类实例的时候,将会回调这些接口的setter方法,将对应的参数设置进去。
ConfigurationClassPostProcessor还是一个特殊的BeanDefinitionRegistryPostProcessor类型的后处理器,因此将会在容器初始化过程中的refresh方法中的invokeBeanFactoryPostProcessors方法中被实例化并且按顺序回调它的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法,实际上这两个方法就是ConfigurationClassPostProcessor的工作入口,它们被Spring的回调机制自动调用。
下面我们从ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法入手来分析它的源码。
postProcessBeanDefinitionRegistry
该方法在IoC容器初始化的invokeBeanFactoryPostProcessors步骤中被自动回调,在经过此前的obtainFreshBeanFactory方法之后,所有基于XML的bean定义(包括< context:component-scan/>等各种扩展标签引入的bean定义)已被加载,但是并没有加载完全,比如@Bean注解方法、非静态内部类的bean定义、@ComponentScan表示包、@Import引入bean等等……,这一步就是主要干这个事儿,从registry注册表中的已注册的配置类的bean定义中的内部类和其他的配置注解比如@Bean、@Conditional、@Import、@ComponentScan、@PropertySource一并解析,进一步提取 bean 定义并注册到注册表中。
核心逻辑还是在内部的processConfigBeanDefinitions方法中完成的!
//---------ConfigurationClassPostProcessor的相关属性
/**
* 被postProcessBeanDefinitionRegistry方法处理的registry的identityHashCode的缓存,用于防止重复处理
*/
private final Set<Integer> registriesPostProcessed = new HashSet<>();
/**
* 被postProcessBeanFactory方法处理的registry的identityHashCode的缓存,用于防止重复处理
*/
private final Set<Integer> factoriesPostProcessed = new HashSet<>();
/**
1. ConfigurationClassPostProcessor的方法
2. <p>
3. 主要功能就是从registry注册表中的已注册的配置类中进一步提取 bean 定义
4. 5. @param registry bean定义注册表,实际类型就是当前上下文中的DefaultListableBeanFactory对象
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
/*
* 校验重复处理
*/
//获取registry注册表的identityHashCode值作为registryId,实际上就是返回默认hashCode方法的返回值,无论有没有重写hashCode方法
//一个registry对象有唯一的registryId值
int registryId = System.identityHashCode(registry);
//如果registriesPostProcessed缓存包含了该registryId值,那么说明此前该registry已被该ConfigurationClassPostProcessor处理过了
//Spring不允许同一个ConfigurationClassPostProcessor重复处理同一个registry,直接抛出异常
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
//如果factoriesPostProcessed缓存包含了该registryId值,那么说明此前该registry已被该ConfigurationClassPostProcessor处理过了
//Spring不允许同一个ConfigurationClassPostProcessor重复处理同一个registry,直接抛出异常
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
//当前registry的registryId加入registriesPostProcessed缓存
this.registriesPostProcessed.add(registryId);
/*
* 继续调用processConfigBeanDefinitions方法,这是核心方法
*/
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions处理配置类bean定义
该方法是处理配置类bean定义的核心方法,定义了大概骨架步骤:
- 获取注册表中所有beanName数组,通过checkConfigurationClassCandidate方法判断依次判断是否是配置类并且设置属性,并封装为一个BeanDefinitionHolder加入configCandidates集合。
- 如果configCandidates集合没有收集到任何配置类定义,那么直接返回,不需要进入下一步。
- 对配置类根据@Order注解进行排序,按照排序顺序先后解析每一个的配置类定义,主要涉及到parse和loadBeanDefinitions这两个核心方法,也就是对配置类的各种注解的解析和bean定义的注册,逻辑很复杂,后面有源码详解。
- 解析完毕之后,如果发现由于解析配置类而新增了bean定义,那么对这些新增的bean定义同样看作配置类,并且继续第三步循环处理,直到最后不再有新增的bean定义。
/**
* ConfigurationClassUtils的属性
* org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass属性名
* 用于标记该配置类是否已被处理过了
*/
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
//-----------ConfigurationClassPostProcessor的相关属性-----------
/**
* 是否有本地beanName生成器,默认false
*/
private boolean localBeanNameGeneratorSet = false;
/**
* AnnotationBeanNameGenerator类型的beanName生成器
* 默认作为@ComponentScan组件扫描注解的解析器componentScanParser 的beanName生成器
*/
private BeanNameGenerator componentScanBeanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
/**
* 用在注册配置类本身作为bean定义的时候,即被引入的bean的beanName生成器。
* registerBeanDefinitionForImportedConfigurationClass方法中使用
*/
private BeanNameGenerator importBeanNameGenerator = IMPORT_BEAN_NAME_GENERATOR;
/**
* FullyQualifiedAnnotationBeanNameGenerator类型的beanName生成器
* 默认情况下,使用完全限定的类名作为默认的 bean 名称。
*/
public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
new FullyQualifiedAnnotationBeanNameGenerator();
/**
* 环境变量,在创建该类实例时通过EnvironmentAware自动设置
*/
@Nullable
private Environment environment;
/**
* 问题报告器,用于抛出异常
*/
private ProblemReporter problemReporter = new FailFastProblemReporter();
/**
* 资源加载器,用于加载外部文件资源
*/
private ResourceLoader resourceLoader = new DefaultResourceLoader();
/**
* 元数据读取器工厂
*/
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
/**
* bean定义加载器
*/
@Nullable
private ConfigurationClassBeanDefinitionReader reader;
/**
* 手动注册的一个beanName
*/
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
/**
* ConfigurationClassPostProcessor的方法
* <p>
* 主要功能就是从registry注册表中的已注册的配置类中进一步提取 bean 定义
*
* @param registry bean定义注册表,实际类型就是当前上下文中的DefaultListableBeanFactory对象
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//临时集合,用于存储配置类的bean定义
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//返回此注册表中已注册的所有 bean 定义的名称数组
String[] candidateNames = registry.getBeanDefinitionNames();
/*
* 1 遍历beanName数组,根据bean定义判断配置类,设置属性,并封装为一个BeanDefinitionHolder加入configCandidates集合
*/
for (String beanName : candidateNames) {
//根据beanName获取对应的bean定义
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
/*
* 获取bean定义的"org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass"属性
* bean定义的父类BeanMetadataAttributeAccessor的方法,如果不为null,说明当前Bean 定义已作为配置类处理过了,这里不再处理
*/
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
/*
* 否则,调用checkConfigurationClassCandidate校验当前bean定义是否对应着一个配置类,并为其设置相应的属性为lite或者full
* 如果具有@Configuration注解以及派生注解,那么算作配置类,设置为full
* 如果具有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解及其派生注解之一,那么算作配置类,那么设置为lite
* 后面会用到这个属性
*/
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//如果是配置类,则封装成为一个BeanDefinitionHolder加入到configCandidates集合中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
/*
* 2 如果configCandidates集合没有收集到任何配置类定义,那么直接返回
*/
if (configCandidates.isEmpty()) {
return;
}
//到这一步,表示存在配置类
/*
* 3 将configCandidates集合中的配置类按照此前checkConfigurationClassCandidate解析的@Order注解的值排序,值越小越靠前
*/
configCandidates.sort((bd1, bd2) -> {
//获取order值进行比较
//如果此前没有解析到@Order注解,那么将返回Ordered.LOWEST_PRECEDENCE,即Integer.MAX_VALUE,表示优先级最低,排在末尾
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
/*
* 确定beanName生成器
*/
SingletonBeanRegistry sbr = null;
//DefaultListableBeanFactory属于SingletonBeanRegistry类型
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
//如果没有自定义的beanName生成器,可通过ConfigurationClassPostProcessor的setBeanNameGenerator方法设置,默认false
if (!this.localBeanNameGeneratorSet) {
//获取设置的beanName生成器,一般只有AnnotationConfigApplicationContext上下文的setBeanNameGenerator方法会设置
//如果是其他上下文容器比如ClassPathXmlApplicationContext就是返回null
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
//如果不为null
if (generator != null) {
//那么设置generator为将要使用的beanName生成器
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
//环境变量,在创建该类实例时通过EnvironmentAware自动设置
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
/*
* 4 解析每一个配置类定义
*/
/*
* 创建配置类解析器,传递:metadataReaderFactory - 元数据解析工厂、problemReporter -问题记录器
* environment - 环境变量、resourceLoader - 资源加载器、registry - 注册表,就是当前DefaultListableBeanFactory对象
* componentScanBeanNameGenerator - beanName生成器,默认就是AnnotationBeanNameGenerator
*/
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
//保存需要解析的配置类,默认保存configCandidates的中的全部配置类定义
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
//保存已被解析的配置类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
/*
* 通过parser解析器解析每一个配置类
*/
parser.parse(candidates);
/*
* 通过parser解析器验证每一个配置类
*/
parser.validate();
//获取解析后的ConfigurationClass配置类集合
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
//移除全部的alreadyParsed集合中的数据
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
//创建配置类bean定义阅读器,用于创建BeanDefinition
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
/*
* 通过reader加载、注册此configClasses中的bean定义,其来源包括
* 包括配置类本身、@Bean方法、@ImportedResource注解、@Import注解
*/
this.reader.loadBeanDefinitions(configClasses);
//将configClasses存入已解析的集合中
alreadyParsed.addAll(configClasses);
//清空candidates集合
candidates.clear();
/*
* 处理新增了bean定义的情况,将会循环解析新增的bean定义
*/
//如果解析之后容器中已注册的bean定义数量大于在解析之前的bean定义数量,说明当前方法向注册表中注册了新的bean定义
if (registry.getBeanDefinitionCount() > candidateNames.length) {
//获取最新的bean定义名集合
String[] newCandidateNames = registry.getBeanDefinitionNames();
//oldCandidateNames保存一份candidateNames的副本
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
//已解析的bean定义的className集合
Set<String> alreadyParsedClasses = new HashSet<>();
//将alreadyParsed中的全部configClass的className加入alreadyParsedClasses集合
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
//遍历最新bean定义名集合
for (String candidateName : newCandidateNames) {
//如果当前遍历的候选beanName没在旧集合中,说明是新添加的
if (!oldCandidateNames.contains(candidateName)) {
//获取当前candidateName的bean定义
BeanDefinition bd = registry.getBeanDefinition(candidateName);
//如果当前bean定义是配置类,并且没有被解析过
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
//那么存入candidates集合中,此时candidates集合由有了数据,那么将回进行下一次循环,解析新添加的bean定义
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
//candidateNames设置最新的newCandidateNames集合
candidateNames = newCandidateNames;
}
}
//如果candidates集合不为空,那么说明在本次解析过程中新添加了bean定义,继续循环解析这些新的bean定义
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
//如果注册表中不包含名为"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry"的单例bean实例
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
//那么手动注册一个单例bean实例,名为"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry"
//实际上就是importStack集合对象
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
//默认就是CachingMetadataReaderFactory
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
//清除外部提供的元数据阅读器工厂MetadataReaderFactory中的缓存
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
getAttribute获取属性
这里的“属性”并不是存储在BeanDefinition中的,而是存放在BeanDefinition实际类型的父类AttributeAccessorSupport的attributes缓存中的,该缓存属于对象级别,每一个bean定义都有自己的attributes缓存,因此不会共享。
attributes默认是空集合,当调用下面的checkConfigurationClassCandidate方法时才会设置值,因此可用于判断当前bean定义是否已被解析过。
/**
* BeanMetadataAttributeAccessor的方法
* 获取属性,GenericBeanDefinition、ScannedGenericBeanDefinition等都属于BeanMetadataAttributeAccessor类型
*
* @param name 属性名
* @return 属性值
*/
@Override
@Nullable
public Object getAttribute(String name) {
//继续调用父类AttributeAccessorSupport的方法
BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
//如果没有就返回null,否则返回value值
return (attribute != null ? attribute.getValue() : null);
}
/**
* AttributeAccessorSupport的属性
* <p>
* 使用字符串键和对象值映射,这就是就是所谓的“属性”的缓存位置
* 该缓存是存放在bean定义的父类AttributeAccessorSupport中的,默认是空集合
*/
private final Map<String, Object> attributes = new LinkedHashMap<>();
/**
1. AttributeAccessorSupport的方法
2. 获取属性
3. 4. @param name 属性名
5. @return 属性值
*/
@Override
@Nullable
public Object getAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.get(name);
}
checkConfigurationClassCandidate判断配置类并设置属性
如果获取到对应属性的值,那么说明当前bean定义没有被解析过,那么调用checkConfigurationClassCandidate解析判断当前bean定义表示的类是否是配置类。该方法位于ConfigurationClassUtils类中,这个类就是专门用于判断给定的bean定义是否是配置类的一个工具类。
解析之后如果属于配置类那么会设置对应属性的值,后面会使用到,后续再进来的时候也不会再次解析。
- 如果具有@Configuration注解及其派生注解标注并且proxyBeanMethods属性的值为true(默认就是true),那么算作配置类,设置属性 CONFIGURATION_CLASS_ATTRIBUTE = full,CONFIGURATION_CLASS_ATTRIBUTE就是“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”;
proxyBeanMethods属性就表示是否需要代理@Bean方法,即对@Bean方法的调用实施bean生命周期行为,这将通过CGLIB子类拦截方法来完成。 - 如果具有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解及其派生注解之一,那么算作配置类,那么设置属性 CONFIGURATION_CLASS_ATTRIBUTE = lite;
- 如果当前bean定义的类是一个配置类,并且具有@Order注解,那么会设置属性 ORDER_ATTRIBUTE = order值,ORDER_ATTRIBUTE就是“org.springframework.context.annotation.ConfigurationClassPostProcessor.order”;
- 这里的属性是设置给当前bean定义实际类型(GenericBeanDefinition、ScannedGenericBeanDefinition等)的父类AttributeAccessorSupport的attributes缓存中的,key为name常量,value为根据name和value封装的一个BeanMetadataAttribute对象。
//-----------ConfigurationClassUtils的相关属性-------------
/**
* 属性key
* org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass
*/
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
/**
* 属性key
* org.springframework.context.annotation.ConfigurationClassPostProcessor.order
* 如果当前bean定义的类是一个配置类,并且具有@Order注解,那么会设置属性 ORDER_ATTRIBUTE = order值
*/
private static final String ORDER_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
/**
* 如果当前bean定义的类上具有@Configuration注解,或者以@Configuration注解为元注解的注解(派生注解)
* 那么表示一个配置类,并且设置属性 CONFIGURATION_CLASS_ATTRIBUTE = full
*/
public static final String CONFIGURATION_CLASS_FULL = "full";
/**
* 如果当前bean定义的类上具有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解之一,或者以这些注解为元注解的注解(派生注解)
* 那么表示一个配置类,并且设置属性 CONFIGURATION_CLASS_ATTRIBUTE = lite
*/
public static final String CONFIGURATION_CLASS_LITE = "lite";
/**
* ConfigurationClassUtils的方法
* <p>
* 如果当前检查的类上没有@
* 检查给定的 bean 定义是否是配置类并相应地标记它
*
* @param beanDef 要检查的bean定义
* @param metadataReaderFactory 调用方正在使用的当前beanFactory工厂
* @return 是否是配置类,true 是 false 否
*/
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
//获取类型名,如果为null或者工厂方法名不为null,说明不是普通bean定义或者是工厂方法bean,返回false
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
//如果是支持注解类型的bean定义,如果是采用组件注解添加的bean定义那么支持,并且元数据来自同一个类
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
//获取类元数据
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
/*
* 如果是普通类型的bean定义,也就是通过XML配置添加的bean定义
*/
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
//获取所属类型
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
//如果类型属于BeanFactoryPostProcessor,或者属于BeanPostProcessor,或者属于AopInfrastructureBean,或者属于EventListenerFactory
//那么返回false,表示不是配置类
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
//获取类元数据
metadata = AnnotationMetadata.introspect(beanClass);
}
/*
* 否则
*/
else {
try {
//直接获取元数据
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
} catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
/*
* 解析类元数据,判断是否是配置类
*/
//获取该类上的@Configuration注解的属性映射map,包括以@Configuration注解为元注解的注解
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
/*
* 如果config不为null,表示存在@Configuration注解获取以@Configuration注解为元注解的注解
* 并且proxyBeanMethods属性的值为true,默认就是true
*/
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
//那么设置当前bean定义的属性,bean定义的父类BeanMetadataAttributeAccessor的方法
//org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass = full
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
/*
* 否则,如果是其他配置类
* */
else if (config != null || isConfigurationCandidate(metadata)) {
//那么设置当前bean定义的属性,bean定义的父类BeanMetadataAttributeAccessor的方法
//org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass = lite
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
/*
* 否则,表示不是配置类,返回false
*/
else {
return false;
}
//到这里,表示属于配置类
//确定给定配置类元数据的顺序
//获取当前bean定义的@Order注解的值
Integer order = getOrder(metadata);
//如果设置了order值
if (order != null) {
//那么设置当前bean定义的属性,bean定义的父类BeanMetadataAttributeAccessor的方法
//org.springframework.context.annotation.ConfigurationClassPostProcessor.order = order值
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
//返回true
return true;
}
isConfigurationCandidate是否是配置类
如果当前bean定义所属的类没有@Configuration注解以及派生注解,那么继续调用isConfigurationCandidate方法检查:如果类上存在@Component、@ComponentScan、@Import、@ImportResource注解及其派生注解,或者至少有一个方法上具有@Bean注解,那么同样算作配置类。
/**
* 配置类的候选者,ConfigurationClassUtils加载的时候就填充了数据
* 如果某个类具有内部的任意一个注解或者派生注解,那么就算作配置类
*/
private static final Set<String> candidateIndicators = new HashSet<>(8);
/*
* 静态块
*/
static {
//加入@Component、@ComponentScan、@Import、@ImportResource注解类名
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
/**
* 确定是否是配置类
*
* @param metadata 类的元数据
* @return true 是 false 否
*/
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// 如果是接口,那么不考虑
if (metadata.isInterface()) {
return false;
}
/*
* 遍历candidateIndicators集合,如果当前类具有@Component、@ComponentScan、@Import、@ImportResource注解及其派生注解的任何一个
* 那么算作配置类
*/
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
//上面的循环没确定结果,那么继续查找@Bean注解标注的方法
try {
//如果该类的存在至少一个具有@Bean注解及其派生注解标注的方法,那么算作配置类
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;
}
}
getOrder获取@Order注解值
getOrder用于获取当前bean定义所属的类上的@Order注解的值,如果存在该注解,那么后续还会添加一个ORDER_ATTRIBUTE属性到当前bean定义中。
/**
* 获取@Order注解的值,用于确定给定配置类元数据的顺序
*
* @param metadata 类元数据
* @return 配置类的@Order注解的值,如果没有@Order注解,那么返回null
*/
@Nullable
public static Integer getOrder(AnnotationMetadata metadata) {
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
return (orderAttributes != null ? ((Integer) orderAttributes.get(AnnotationUtils.VALUE)) : null);
}
setAttribute设置属性
如果当前bean定义所属的类是配置类,那么设置key为CONFIGURATION_CLASS_ATTRIBUTE的属性,也就是“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”,value就是“full”或者“lite”值。
如果当前bean定义所属的类是配置类并且具有@Order注解,那么设置key为ORDER_ATTRIBUTE的属性,也就是“org.springframework.context.annotation.ConfigurationClassPostProcessor.order”,value就是order值。
实际存入缓存的值是key和value封装的一个BeanMetadataAttribute对象。
/**
* BeanMetadataAttributeAccessor的方法
* 设置属性,GenericBeanDefinition、ScannedGenericBeanDefinition等都属于BeanMetadataAttributeAccessor类型
*
* @param name 属性名
* @param value 属性值
*/
@Override
public void setAttribute(String name, @Nullable Object value) {
//调用父类BeanMetadataAttributeAccessor的方法
//key为name,value为根据name和value封装的一个BeanMetadataAttribute对象
super.setAttribute(name, new BeanMetadataAttribute(name, value));
}
/**
* AttributeAccessorSupport的属性
* <p>
* 使用字符串键和对象值映射,这就是就是属性的缓存位置
*/
private final Map<String, Object> attributes = new LinkedHashMap<>();
/**
* AttributeAccessorSupport的方法
* 设置属性到缓存中
*
* @param name 属性名
* @param value 属性值
*/
@Override
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
} else {
removeAttribute(name);
}
}
getOrder获取顺序值
获取配置类的order顺序值,也就是配置类上的@Order注解的值,如果未声明则返回Ordered.LOWEST_PRECEDENCE,即Integer.MAX_VALUE,将会排序在集合尾部。
/**
* ConfigurationClassUtils的属性
* 表示具有@Order注解的属性的key
*/
private static final String ORDER_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
/**
* ConfigurationClassUtils的方法
* 获取配置类的order顺序值,也就是
* 配置类上的@Order注解的值,如果未声明则返回Ordered.LOWEST_PRECEDENCE,即Integer.MAX_VALUE
*
* @param beanDef 要检查的bean定义
* @return 配置类的order顺序值
*/
public static int getOrder(BeanDefinition beanDef) {
//获取ORDER_ATTRIBUTE属性值order,这个属性我们在checkConfigurationClassCandidate方法中已尝试解析并设置到属性中了
//这里直接取出来使用,如果没有@Order注解会返回null
Integer order = (Integer) beanDef.getAttribute(ORDER_ATTRIBUTE);
//如果order不为null,那么直接返回,否则返回Ordered.LOWEST_PRECEDENCE,即Integer.MAX_VALUE
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
}
new ConfigurationClassParser创建解析器
创建一个ConfigurationClassParser解析器对象,专门用于解析找到的配置类定义。初始化一些属性。
//----------ConfigurationClassParser相关属性-------------
private final MetadataReaderFactory metadataReaderFactory;
private final ProblemReporter problemReporter;
private final Environment environment;
private final ResourceLoader resourceLoader;
private final BeanDefinitionRegistry registry;
private final ComponentScanAnnotationParser componentScanParser;
private final ConditionEvaluator conditionEvaluator;
/**
* 创建一个ConfigurationClassParser实例,用于解析配置类集合。
*
* @param metadataReaderFactory 元数据解析工厂
* @param problemReporter 问题报告器
* @param environment 环境变量
* @param resourceLoader 资源加载器
* @param componentScanBeanNameGenerator beanName生成器,默认就是AnnotationBeanNameGenerator
* @param registry 注册表,就是当前DefaultListableBeanFactory对象
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
//为一些属性赋值
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
//@ComponentScan组件扫描注解的解析器
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
//@Conditional条件注解评估器
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
parser.parse解析配置类
通过ConfigurationClassParser解析器去解析所有的配置类定义。其内部是循环调用processConfigurationClass方法按照此前排序的先后顺序处理每一个配置类,传递的参数是一个ConfigurationClass对象和一个排除类型过滤器。ConfigurationClass表示一个配置类,保存了配置类的相关信息。
/**
* ConfigurationClassParser的方法
* <p>
* 解析配置类的bean定义集合,实际上也就是AnnotationConfigApplicationContext容器的构造器参数个数
*
* @param configCandidates 配置类的bean定义集合
*/
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//遍历集合
for (BeanDefinitionHolder holder : configCandidates) {
//获取bean定义
BeanDefinition bd = holder.getBeanDefinition();
//根据类型调用不同的parse方法,其内部都是调用的processConfigurationClass方法
try {
//如果属于AnnotatedBeanDefinition,比如AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition、ConfigurationClassBeanDefinition
if (bd instanceof AnnotatedBeanDefinition) {
//调用另一个parse方法解析,内部调用processConfigurationClass方法
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
//否则,如果是AbstractBeanDefinition类型,比如GenericBeanDefinition,并且已经解析了class
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
//调用另一个parse方法解析,内部调用processConfigurationClass方法
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
//否则
else {
//调用另一个parse方法解析,内部调用processConfigurationClass方法
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
//最后处理在processImports方法解析@Import注解时遇到的DeferredImportSelector,内部同样是调用processImports方法继续递归处理
this.deferredImportSelectorHandler.process();
}
/*
* 三个parse方法内部都是调用的processConfigurationClass方法,传递的参数是一个ConfigurationClass对象
* 和一个排除类型过滤器,ConfigurationClass表示一个配置类,保存了配置类的相关信息
*/
/**
* ConfigurationClassParser的属性
* <p>
* 类型排除过滤器,这是一个i嗯lambda对象
* 如果类名以"java.lang.annotation."或者"org.springframework.stereotype."开头,就返回true,或者返回false
*/
private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->
(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
processConfigurationClass处理一个配置类
首先通过shouldSkip判断是否有必要继续解析这个配置类,参数设置的配置生效的阶段为ConfigurationPhase.PARSE_CONFIGURATION,即类的阶段。这是对于@Conditional注解的支持。这个方法我们在此前的IoC容器初始化的doScan扫描包阶段的isCandidateComponent方法中就见过了。
随后处理被Import引入的配置类的情况;
最后调用doProcessConfigurationClass核心方法,真正的去解析该配置类及其父类。
//------------ConfigurationClassParser的相关属性-----------
/**
* 解析@Conditional条件注解的评估器
* Spring 4.0 新增的@Conditional条件注解,可以标注在类或者方法上,在容器启动时用于控制一批或者一个bean实例是否被注入
* 通过判断该注解中指定的条件是否满足,如果不满足则不会将对应的bean注入到容器中,如果满足则会将对应的bean进行注入
*/
private final ConditionEvaluator conditionEvaluator;
/**
* 已解析的配置类缓存map
*/
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
/**
* 已知的超类配置类缓存map
*/
private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<>();
/**
* ConfigurationClassParser的方法
* <p>
* 处理每一个配置类
*
* @param configClass 表示配置类的对象,包含配置类的一些信息
* @param filter 类型过滤器
*/
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
/*
* 1 通过判断类上的@Conditional条件注解是否满足Condition条件来控制是否跳过该配置类的解析
* 参数设置的配置生效的阶段为ConfigurationPhase.PARSE_CONFIGURATION,即类的阶段
*/
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
/*
* 2 处理被@Import引入的bean定义的情况
*/
//将当前configClass作为key,尝试获取configClass集合中的configClass的缓存
//ConfigurationClass的hashCode和equals方法都被重写了,比较的是内部的className字符串的hashCode
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//如果existingClass不为null,说明此前该类已被@Import引入
if (existingClass != null) {
//如果当前configClass的importedBy属性集合不为空,说明当前bean定义是被@Import引入的
if (configClass.isImported()) {
//如果此前的existingClass的importedBy属性集合也不为空,说明此前同类型的bean定义也是被@Import引入的
if (existingClass.isImported()) {
//对引入该配置类的引入类进行合并,也就是将当前@Import所在的引入类添加到内部的importedBy属性集合中
existingClass.mergeImportedBy(configClass);
}
//随后直接返回,即对于同一个被@Import引入的类在configurationClasses集合中只存一个
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
//否则,表示当前的bean定义不是被@Import引入的,那么表示属于显示注入的,比如通过组件注解
//因此需要覆盖此前保存的通过@Import引入的bean定义
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
//移除configurationClasses中之前的configClass
this.configurationClasses.remove(configClass);
//移除configurationClasses中之前的configClass
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
/*
* 3 递归处理配置类及其超类
*/
//获取当前配置类的SourceClass,这是一个简单的包装器,允许以统一的方式处理带注释的源类,而不管它们如何加载。
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
/*
* 核心方法,处理每一个配置类及其超类
*/
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
//当前的configClass存入configurationClasses集合,key和value都是同一个对象
this.configurationClasses.put(configClass, configClass);
}
shouldSkip解析@Conditional判断是否跳过
通过判断class上的@Conditional条件注解是否满足Condition条件来控制是否跳过该配置类的解析。在这里,设置的配置生效的阶段参数为ConfigurationPhase.PARSE_CONFIGURATION,即解析配置类的阶段,如果此时某个Condition是REGISTER_BEAN阶段,那么该Condition无论有没有满足条件都不生效(都算作满足)。
这里是对@Conditional条件注解的支持,Spring 4.0 新增的@Conditional条件注解,可以标注在类或者方法上,在容器启动时用于控制一批或者一个bean实例是否被注入,通过判断该注解中指定的条件是否满足,如果不满足则不会将对应的bean注入到容器中,如果满足则会将对应的bean进行注入或者进一步处理。
/**
1. ConditionEvaluator的方法
2. <p>
3. 确定是否应基于@Conditional注解跳过此项(类或者方法的解析)
4. <p>
5. Spring 4.0 新增的@Conditional条件注解,可以标注在类或者方法上,在容器启动时用于控制一批或者一个bean实例是否被注入
6. 通过判断该注解中指定的条件是否满足,如果不满足则不会将对应的bean注入到容器中,如果满足则会将对应的bean进行注入
7. 8. @param metadata 类或者方法元数据
9. @param phase 配置生效的阶段
10. ConfigurationPhase.PARSE_CONFIGURATION 解析配置类的阶段
11. ConfigurationPhase.REGISTER_BEAN 解析配置类的方法阶段
12. @return 如果此项应跳过,则返回true,否则返回false
*/
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
//如果元数据所属的类或者方法(根据元数据的类型)上没有@Conditional注解,那么直接返回false
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
/*
* 如果配置生效的阶段为null,那么根据元数据类型自动选择一个阶段重新调用shouldSkip方法
* 在前面讲的IoC容器初始化的扫描包阶段的isCandidateComponent方法中就调用了这个方法,并且phase参数传递的null
*/
if (phase == null) {
//如果是AnnotationMetadata类型,表示当前元数据是类元数据,比如SimpleAnnotationMetadata,那么phase设置为PARSE_CONFIGURATION
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
//否则,表示当前元数据是方法元数据,比如MethodMetadata,那么phase设置为REGISTER_BEAN
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
/*
* 提取@Conditional注解中配置的全部Condition条件
*/
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
/*
* 对全部Condition条件进行排序,可以看到采用的是AnnotationAwareOrderComparator比较器
* 该比较器支持Ordered、PriorityOrdered接口,以及@Order、@Priority注解的排序
* 排序规则是order值越小排序越靠前,优先级越高
*/
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions) {
//必须的配置生效的阶段,只有ConfigurationCondition类型的条件才具备该属性
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
//获取配置生效的阶段
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
//如果配置生效的阶段为null或者与参数的阶段相同,并且当前Condition的条件不满足,那么返回true,表示可以跳过此项目
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
//所有Condition都满足,那么返回false,表示不跳过
return false;
}
@Conditional注解介绍
Spring 4.0 新增的@Conditional条件注解,可以标注在类或者方法上,在容器启动时用于控制一批或者一个bean实例是否被注入,通过判断该注解中指定的条件是否满足,如果不满足则不会将对应的bean注入到容器中,如果满足则会将对应的bean进行注入或者进一步处理。
- 标注在类上时,如果它内部的Condition条件不满足,那么该类的bean定义以及该类下面的bean定义,比如通过@Bean注解指定的方法表示的bean定义都不会被解析注册。
- 标注在方法上时,如果它内部的Condition条件不满足,那么该方法表示的bean定义(通过@Bean注解)不会被解析注册。
@Conditional的参数是一个类型的数组,类型必须是属于Condition接口体系,这个Condition接口有一个matches方法,该方法返回一个boolean值,返回true表示该匹配条件,返回false则表示不匹配。只有所有的Conditon条件都匹配才算做最终满足条件。该方法的ConditionContext条件变量可以获取各种容器参数,比如Registry、BeanFactory、Environment、ResourceLoader、ClassLoader。
@FunctionalInterface
public interface Condition {
/**
* 确定条件是否匹配
*
* @param context 条件上下文,可以从李米娜获取这种各样的内部参数
* @param metadata 被检查的类或者方法的元数据
* @return 条件匹配返回true,条件不匹配返回false
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Condition还有一个特性化子接口ConfigurationCondition。这个接口多了一个getConfigurationPhase方法,用于指定该Conditon的生效阶段,可选类型保存在这个接口内部的ConfigurationPhase枚举中。该接口用的比较少。
如果某个条件是ConfigurationCondition类型,那么先判断阶段是否匹配,如果匹配,再判断条件是否匹配,如果阶段不匹配,那么该条件不生效,即默认条件匹配。这一点看上面的源码就能看出来。
public interface ConfigurationCondition extends Condition {
/**
* 返回该条件的配置生效阶段
*/
ConfigurationPhase getConfigurationPhase();
/**
* 配置的阶段枚举
*/
enum ConfigurationPhase {
/**
* 解析配置类的阶段
*/
PARSE_CONFIGURATION,
/**
* 注册Bean的阶段
*/
REGISTER_BEAN
}
}
@Conditional注解使用
我们自定义两个Condition:
/**
* @author lx
* @date 2020/11/5 16:16
*/
public class MyConditional {
/**
* 用于排序的@Priority注解
*/
@Priority(2)
public static class MyConditional1 implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
//如果设置了key为"p"的环境变量,那么该条件满足,否则不满足
return environment.getProperty("p") != null;
}
}
@Priority(1)
public static class MyConditional2 implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
}
定义一个测试类com.spring.source.conditional.ConditionTest,加上@Conditional注解,包含两个条件:
/**
* @author lx
* @date 2020/11/5 16:16
*/
@Component
@Conditional({MyConditional.MyConditional1.class, MyConditional.MyConditional2.class})
public class ConditionTest { }
配置文件spring-config-conditional.xml:
<context:component-scan base-package="com.spring.source.conditional"/>
测试:
@Test
public void conditional() {
//设置环境变量
System.setProperty("p", "x");
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-config-conditional.xml");
System.out.println(ac.getBean("conditionTest"));
}
由于设置了p环境变量,因此满足条件,所以该bean顶以被注册,能够获取到对应的实例,结果如下:
com.spring.source.conditional.ConditionTest@1198b989
如果我们将设置环境变量的代码注释掉,继续测试,将会抛出异常:
No bean named 'conditionTest' available
即,由于条件不满足,造成该bean定义被没有注册和初始化,因此获取不到对应的实例。
doProcessConfigurationClass处理一个配置类
这是对一个配置类的真正的处理方法。将会对配置类的内部类、超类、以及相关注解进行处理,大概步骤为:
- 处理内部类:
如果当前配置类上存在@Component以及派生注解,包括@Repository、@Service、@Controller、@Configuration等注解,那么调用processMemberClasses方法首先处理内部类配置类。
如果内部类也是一个配置类,那么将会递归调用processConfigurationClass方法先解析内部配置类。非静态内部类的bean定义的注册就是从该方法开始的(因为在doScan扫描包的方法中不会解析、注册非静态内部类的bean定义)。 - 处理@PropertySources、@PropertySource注解:
如果当前配置类上存在@PropertySources或者@PropertySource以及它们的派生注解,那么调用processPropertySource解析这些注解。@PropertySources、@PropertySource用于引入本地本地properties属性源文件,可用来替代XML的配置,比如< context:property-placeholder/>标签。@PropertySources、@PropertySource注解可重复出现。
这里的properties属性源文件路径支出占位符,将会environment环境变量中查找,而解析出来的本地属性源同样会存入environment环境变量中。 - 处理@ComponentScans、@ComponentScan注解:
@ComponentScans、@ComponentScan注解用于配置组件扫描,可用来替代XML的配置,比如< context:component-scan/>标签。
如果shouldSkip方法返回false,即不应该跳过,这里的phase生效的阶段参数为REGISTER_BEAN,那么调用componentScanParser.parse方法解析配置类上的@ComponentScan注解、扫描bean定义并进行注册,否则跳过解析。
该注解配置的包路径的最终同样是依靠ClassPathBeanDefinitionScanner的doScan方法进行扫描的,因此和此前< context:component-scan/>的注解解析可以说是殊途同归。 - 处理@Import注解:
@Import注解可用于引入一批指定类型的bean定义,这是另一种注册bean定义的方式;通过processImports方法处理配置类上的@Import注解。
而@Import注解也有三种添加bean定义的方法:普通类型、ImportSelector类型、ImportBeanDefinitionRegistrar类型。注意通过@Import引入的bean定义在这里并不会被注册 - 处理@ImportResource注解:
@ImportResource注解可用于引入一批Spring的XML配置文件,如果即基于注解开发又写了XML配置文件,那么可以使用该注解引入Spring的XML配置文件。
这里仅仅对于@ImportResource注解引入的配置文件路径进行占位符的替换(从environment环境变量中查找),并方法替换后的路径和reader读取器通过addImportedResource方法存入configClass的importedResources缓存中,后续再继续处理。 - 处理@Bean注解:
处理配置类内部的@Bean注解标注的方法,@Bean注解用于通过方法引入bean定义,类似于工厂方法。
这里同样是仅仅将解析后的方法元数据通过addBeanMethod方法存入configClass的beanMethods缓存中,后续再继续处理。 - 处理接口上的默认方法:
默认方法是Java8的新特性,主要就是对接口上标注了@Bean注解的默认方法进行类似于上一步的处理,存入configClass的beanMethods缓存中,后续再处理。 - 处理父类:
置类的父类将会被同样解析,配置类的父类将会被同样解析,而对于以"java."类路径开头的父类,比如Object,也就是rt.jar包中的Java核心类作为父类时不会被解析。
如果存在非核心父类,那么返回父类的sourceClass,将会在外层的processConfigurationClass中进行下一次循环解析父类。
如果没有符合规则的父类或者都解析完毕,那么返回null,将会在外层的processConfigurationClass中跳出循环,进行后续步骤。
该方法对于通过@ComponentScans、@ComponentScan注解引入的bean定义会添加到了容器中,但是对于@Import、@Bean、@ImportResource注解引入的bean定义并没有注册到容器中,而是在后面的this.reader.loadBeanDefinitions()方法中才会真正的解析和注册。
//--------------ConfigurationClassParser的相关属性
/**
* 解析@Conditional条件注解的评估器
* Spring 4.0 新增的@Conditional条件注解,可以标注在类或者方法上,在容器启动时用于控制一批或者一个bean实例是否被注入
* 通过判断该注解中指定的条件是否满足,如果不满足则不会将对应的bean注入到容器中,如果满足则会将对应的bean进行注入
*/
private final ConditionEvaluator conditionEvaluator;
/**
* 解析@ComponentScan注解的解析器,在ConfigurationClassParser对象的构造器中被初始化
*/
private final ComponentScanAnnotationParser componentScanParser;
/**
* ConfigurationClassParser的方法
* <p>
* 通过从sourceClass读取注解、成员和方法,应用处理并构建完整的ConfigurationClass。当发现多个相关sourceClass时,可以多次调用此方法。
*
* @param configClass 表示当前配置类的对象
* @param sourceClass 源类
* @param filter 类型过滤器
* @return 超类的sourceClass,如果未找到或以前处理过就返回null
*/
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
/*
* 1 处理内部类
* 如果元数据所属的类上存在@Component以及派生注解,那么调用processMemberClasses方法首先处理内部类配置类
* 常见包括@Component、@Repository、@Service、@Controller、@Configuration等注解
*/
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
/*
* 递归处理任何成员(嵌套)类,也就是内部类。内部配置类最终还是会调用processConfigurationClass方法
*/
processMemberClasses(configClass, sourceClass, filter);
}
/*
* 2 处理@PropertySources、@PropertySource注解
* 如果元数据所属的类上存在@PropertySources或者@PropertySource以及它们的派生注解,那么调用processPropertySource解析这些注解
* @PropertySources、@PropertySource用于引入本地properties属性源文件,可用来替代XML的配置,比如<context:property-placeholder/>标签
* @PropertySources、@PropertySource注解可重复出现
*/
/*
* 调用attributesForRepeatable方法获取元数据所属的类上的全部@PropertySources、@PropertySource注解的属性集合AnnotationAttributes的集合
* 遍历获取到的全部AnnotationAttributes属性集合
*/
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
/*
* 调用processPropertySource处理通过@PropertySource注解引入的属性源,会将引入的属性源加入到environment环境变量中
*/
processPropertySource(propertySource);
} else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
/*
* 3 处理@ComponentScans、@ComponentScan注解
* @ComponentScans、@ComponentScan注解用于组件扫描,可用来替代XML的配置,比如<context:component-scan/>标签
*/
/*
* 调用attributesForRepeatable方法获取元数据所属的类上的全部@ComponentScans、@ComponentScan注解的属性集合AnnotationAttributes的集合
*/
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
//如果存在@ComponentScan注解,并且shouldSkip方法返回false,即不应该跳过,这里的phase生效的阶段参数为REGISTER_BEAN,即注册bean的阶段
//这两个条件都满足,那么可以解析继续@ComponentScan注解,进而继续扫描包,注册bean定义
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
//遍历componentScans集合
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
/*
* 通过解析器解析@ComponentScan注解,获取解析到的bean定义集合
*/
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 检查扫描到的bean定义集合,以查找任何的配置类,并根据需要递归的解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//调用checkConfigurationClassCandidate判断是否是配置类并设置属性
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
/*
* 如果扫描到的bean定义是配置类,那么调用parse方法解析配置类,内部还是递归调用的processConfigurationClass方法
*/
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
/*
* 4 处理@Import注解
* @Import注解可用于引入一批指定类型的bean定义
*/
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
/*
* 5 处理@ImportResource注解
* @ImportResource注解可用于引入一批Spring的XML配置文件,如果即基于注解开发又写了XML配置文件,那么可以使用该注解引入Spring的XML配置文件
*/
/*
* 调用attributesFor方法获取元数据所属的类上的@ImportResource注解的属性集合AnnotationAttributes
*/
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
//如果不为null,说明存在@ImportResource注解
if (importResource != null) {
//获取locations属性数组,就是XML配置文件的路径字符串
String[] resources = importResource.getStringArray("locations");
//获取reader属性,用来读取配置文件中的bean定义
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
//遍历配置文件路径
for (String resource : resources) {
//使用environment环境变量解析路径字符串的占位符
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
/*
* 仅仅是存入configClass的importedResources缓存中,后续再处理
*/
configClass.addImportedResource(resolvedResource, readerClass);
}
}
/*
* 6 处理配置类内部的@Bean注解标注的方法
* @Bean注解用于通过方法引入bean定义,类似于工厂方法
*/
// Process individual @Bean methods
//获取所有被@Bean注解标注的方法的元数据集合
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
/*
* 仅仅是存入configClass的beanMethods缓存中,后续再处理
*/
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
/*
* 7 处理接口上的默认方法,Java8的新特性
* 主要就是对接口上标注了@bean注解的默认方法进行类似于上一步的处理,存入configClass的beanMethods缓存中,后续再处理
*/
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
/*
* 8 处理父类
* 配置类的父类将会被同样解析,而对于以"java."类路径开头的父类,比如Object,也就是rt.jar包中的Java核心类作为父类时不会被解析
*/
// 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
//返回父类的sourceClass,将会在外层的processConfigurationClass中进行下一次循环解析父类。
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
/*
* 10 如果没有符合规则的父类或者都解析完毕,那么返回null,将会在外层的processConfigurationClass中跳出循环,进行后续步骤。
*/
return null;
}
//----------ConfigurationClass的相关属性-------------
/**
* 通过@ImportResource注解引入的配置文件路径字符串到reader解析器的映射
*/
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
/**
* 所有被@Bean注解标注的方法的元数据集合
*/
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
/**
* 通过@ImportResource注解解析出来的配置文件路径存入configClass的importedResources缓存中
*/
public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
/**
* 被@Bean注解标注的方法元数据存入beanMethods集合
*/
public void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}
processMemberClasses处理内部类配置类
doProcessConfigurationClass方法的第一步就是尝试调用processMemberClasses来处理当前配置类的内部类,如果内部类是一个配置类,那么首先解析内部配置类,最终内部类也会调用processConfigurationClass方法递归解析,并且外部配置类会被设置到内部配置类的importedBy集合中,表示内部类算作被外部类import引入进来的。
还记得我们之前在IoC容器初始化的时候,讲的扩展标签解析的doScan方法内的第二个isCandidateComponent方法吗,该方法将非静态内部类都排除了,因此,非静态内部类的bean定义还没有注册到容器中,但是我们却能取到对应的bean实例,那么它是在哪里解析的呢?就是在这里,processMemberClasses方法就是真正的解析、注册非静态内部类的bean定义的关键地方。
/**
1. ConfigurationClassParser的方法
2. <p>
3. 解析、注册 配置类内部的成员(嵌套)类,也就是内部类。最终还是会调用外部的doProcessConfigurationClass方法
*/
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
/*
* 获取全部内部类的SourceClass集合memberClasses
*
* 还记得我们之前在IoC容器初始化的时候,讲的扩展标签解析的doScan方法内的第二个isCandidateComponent方法吗
* 该方法将非静态内部类都排除了,因此,非静态内部类的bean定义还没有注册到容器中。
*
* 而这里的getMemberClasses将获取所有的内部类,包括静态的和非静态的,其中,非静态内部类的bean定义就是在这里注册的
*/
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
//如果memberClasses不为空
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
candidates.add(memberClass);
}
}
//同样需要排序,这里的OrderComparator就不支持注解了
OrderComparator.sort(candidates);
/*
* 遍历candidates,按照排序顺序依次处理
*/
for (SourceClass candidate : candidates) {
//如果importStack包含此外部配置类,那么说明import循环依赖,直接抛出异常: "A circular @Import has been detected……"
//importStack属性用于判断import重复依赖
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
} else {
//当前外部配置类configClass入栈
this.importStack.push(configClass);
try {
/*
* 调用processConfigurationClass方法,处理当前内部配置类
* 这里的asConfigClass方法将当前外部配置类的设置到内部配置类的importedBy集合中,表示算作被外部类import引入进来的
*/
processConfigurationClass(candidate.asConfigClass(configClass), filter);
} finally {
//当前外部配置类configClass出栈
this.importStack.pop();
}
}
}
}
}
processPropertySource处理@PropertySource属性源注解
该方法用于解析配置类上的@PropertySources以及@PropertySource注解(@PropertySources相当于一个@PropertySource注解的集合)。
通过这两个注解的方式,引入本地属性源,用来替代XML的资源引入,比如< context:property-placeholder/>标签,这个注解可重复出现。属性源将会被添加到到environment环境变量中。
@PropertySource的相关属性如下:
- value:一个字符串数组,存放本地配置文件的路径字符串,一个@PropertySource注解可以引入多个属性配置文件;该属性只有有一个值。同样,这里的路径字符串支持${…:…}占位符解析,但是只会使用environment环境变量中的属性源。一般我们只需要设置value属性即可!
- name:属性源的名称,如果没有指定,那么将会自动生成;
- encoding:文件加载解析的字符集;
- factory:用于创建属性源的工厂的类型,默认为PropertySourceFactory.class;
/**
* PropertySourceFactory的属性
* 默认属性源工厂
* 如果没有指定PropertySource的factory属性,或者指定的属性值为PropertySourceFactory.class
* 那么以该DefaultPropertySourceFactory作为属性源工厂
*/
private static final PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new DefaultPropertySourceFactory();
/**
* ConfigurationClassParser的方法
* <p>
* 处理给定的@PropertySource注解元数据
*
* @param propertySource 找到的@PropertySource注解的元数据,实际上就是一个map,包含了当前注解的属性和对应的值
*/
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;
}
//获取本地配置文件的路径字符串数组,一个@PropertySource注解可以引入多个属性配置文件
String[] locations = propertySource.getStringArray("value");
//断言只有有一个文件路径
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
//获取ignoreResourceNotFound属性的值,表示是否允许配置文件找不到
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
//获取factory属性的值,也就是PropertySourceFactory的class,用来创建属性源,默认值就是PropertySourceFactory.class
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
//获取PropertySourceFactory,用于创建属性源工厂
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
/*
* 遍历本地配置文件的路径字符串数组,依次加载配置文件,添加属性源
*/
for (String location : locations) {
try {
/*
* 通过环境变量,解析路径字符串中的占位符,使用严格模式,遇到没有默认值的无法解析的占位符将抛出IllegalArgumentException异常
* 这说明我们指定的@PropertySource注解中的location支持${.. : ..}占位符,但是只会从environment环境变量中查找属性,这一点要注意
*/
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
//将配置文件加载成为一个Resource资源
Resource resource = this.resourceLoader.getResource(resolvedLocation);
/*
* 根据当前name、resource、encoding创建一个属性源,如果没有name那么将会生成默认的name,随后添加属性源
*/
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;
}
}
}
}
addPropertySource添加属性源到environment
该方法的源码就印证了我们在之前关于PropertySourcesPlaceholderConfigurer的文章的结尾的话语,即@PropertySource注解解析后的属性源将会被添加到environment环境变量中的属性源集合propertySources中。
这个propertySources集合我们在IoC容器初始化的第一篇文章的setLocations方法中就见过了,该集合在setLocations方法中就初始化了著名的systemProperties — JVM系统属性属性源以及systemEnvironment - 系统环境属性源。在根据environment替换占位符的时候,就是从这个属性源集合中依次遍历、查找的,因此systemProperties — JVM系统属性属性源的优先级最高。
通过@PropertySource注解添加的属性源,越后添加的属性源查找的优先级越高,但是仍低于systemProperties和systemEnvironment这两个系统级别的属性源。
/**
* ConfigurationClassParser的属性
* 属性源的名称集合
*/
private final List<String> propertySourceNames = new ArrayList<>();
/**
* 环境变量,在创建解析器的时候就初始化了
*/
private final Environment environment;
/**
1. ConfigurationClassParser的方法
2. <p>
3. 添加属性源到environment环境变量中
4. 5. @param propertySource 当前@PropertySource指定的配置文件对应的属性源
*/
private void addPropertySource(PropertySource<?> propertySource) {
//获取属性源的name
String name = propertySource.getName();
/*
* 获取environment环境变量中的getPropertySources属性源集合,这个属性源集合,我们在IoC容器的最最最开始的setLocations方法中就见过了
* 该集合在setLocations方法中就初始化了著名的systemProperties — JVM系统属性属性源以及systemEnvironment - 系统环境属性源
* 在根据environment替换占位符的时候,就是从这个属性源集合中依次遍历、查找的,因此systemProperties — JVM系统属性属性源的优先级最高
*/
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
/*
* 如果属性源名称集合中已包含该名称name,那么就扩展这个名字的属性源
* 因为一个@PropertySource可能指定多个配置文件
*/
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
//获取该名字的属性源
PropertySource<?> existing = propertySources.get(name);
//如果不为null
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
if (existing instanceof CompositePropertySource) {
//当前属性源加入到existing属性源集合的开头
((CompositePropertySource) existing).addFirstPropertySource(newSource);
} else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
//当前属性源加入到属性源集合的开头
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
/*
* 到这里,表示属性源名称集合中不包含该名称name
* 那么添加一个新name的属性源到环境变量的propertySources属性源集合中,同时将name加入到propertySourceNames集合中
* 越后添加的属性源查找的优先级越高,但是低于systemProperties和systemEnvironment这两个系统级别的属性源
*/
//如果是第一个加载的@PropertySource注解属性源
if (this.propertySourceNames.isEmpty()) {
//那么加入到propertySources属性源集合的尾部,查找的优先级最低
propertySources.addLast(propertySource);
}
//否则
else {
//获取最后一个添加的属性源名称
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
//在propertySources中的名为firstProcessed的属性源的索引处添加该属性源,原索引以及之后的属性源向后移动一位
//即越后添加的属性源优先级越高
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
ComponentScanAnnotationParser.parse处理@ComponentScan组件扫描注解
ComponentScanAnnotationParser的parse方法用于处理@ComponentScan组件扫描注解,根据指定的包路径扫描出全部复合条件的bean定义。
该方法主要是进行@ComponentScan注解的属性解析,真正的扫描包还是通过ClassPathBeanDefinitionScanner的doScan方法进行扫描的,无论是注解配置还是XML的配置,扫描包都是调用的这个方法,我们在此前< context:component-scan/>的注解解析文章中已经讲过了,该方法将会扫描并且注册这些指定包路径下的bean定义。
@ComponentScan的相关属性如下,实际上和< context:component-scan/>的相关属性都差不多,我们在讲这个标签的时候就详细讲解过了:
- useDefaultFilters:是否注册默认类型过滤器,默认值true。
注册默认过滤器就是尝试添加@Component、@ManagedBean、@Named这三个注解类型过滤器到includeFilters缓存集合中!
这表示将会注册所有具有@Component注解及其派生注解的注解标志的类,比如@Component、@Repository、@Service、@Controller、@Configuration,还支持扫描注册 Java EE 6 的注解,比如@ManagedBean,以及JSR-330的注解,比如@Named。 - nameGenerator :指定的beanName生成器的class,默认值是BeanNameGenerator.class,将会使用AnnotationBeanNameGenerator生成beanName。
- scopedProxy、scopeResolver:使用代理模式,通常在web应用中使用。
- resourcePattern:控制符合组件检测条件的类文件,Spring推荐使用includeFilters和excludeFilters。
- includeFilters:包含的类型过滤器,默认空集合。如果扫描到的组件满足该集合中任意的类型过滤器,那么算作有资格作为候选组件。后遍历includeFilters。
- excludeFilters:排除的类型过滤器,默认空集合。如果扫描到的组件满足该集合中任意的类型过滤器,那么算作没有资格作为候选组件。先遍历excludeFilters。
默认添加一个AbstractTypeHierarchyTraversingFilter匿名对象,将会排除当前的配置类的扫描。 - lazyInit:扫描到的组件是否都延迟初始化,默认false。
- basePackages:要扫描的包路径字符串数组。传递的字符串支持${…:…}占位符,将会使用environment环境变量解析,还支持以","、";"、" “、”\t"、"\n"中的任意字符作为分隔符来表示传递了多个包路径。默认是个空数组。
- basePackageClasses:可以指定了一批类的class,Spring将解析class所在的包路经作为扫描包路径,功能和basePackages一致。默认是个空数组。
如果basePackages和basePackageClasses都没有指定值,那么默认扫描当前@ComponentScan注解所在的类所在的包下面的所有bean定义。
//--------ComponentScanAnnotationParser的相关属性,在构造器中初始化-----------
private final Environment environment;
private final ResourceLoader resourceLoader;
private final BeanNameGenerator beanNameGenerator;
private final BeanDefinitionRegistry registry;
/**
* ConfigurableApplicationContext的属性
* <p>
* 分隔符常量,支持","、";"、" "、"\t"、"\n"中的任意分隔符
*/
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
* ComponentScanAnnotationParser的方法
* <p>
* 解析@ComponentScan注解,获取解析到的bean定义集合
*
* @param componentScan 一个@ComponentScan注解的属性集合
* @param declaringClass 当前配置类的className
* @return 解析到的bean定义集合
*/
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
/*
* 创建一个类路径Bean定义扫描器
* 通过useDefaultFilters属性的值判断是否注册默认的类型过滤器,默认值true,即注册默认类型过滤器
*
* 这个默认类型过滤器我们在此前IoC容器初始化源码的<context:component-scan/>扩展标签解析的时候就讲了:
* 注册默认过滤器就是尝试添加@Component、@ManagedBean、@Named这三个注解类型过滤器到includeFilters缓存集合中!
*
* 这表示将会注册所有具有@Component注解及其派生注解的注解标志的类,比如@Component、@Repository、@Service、@Controller、@Configuration,
* 还支持扫描注册 Java EE 6 的注解,比如@ManagedBean,以及JSR-330的注解,比如@Named
*/
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
/*
* 获取beanName生成器,默认是AnnotationBeanNameGenerator
*/
//获取nameGenerator属性值,即beanName生成器的class,默认值为BeanNameGenerator.class
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
//判断是否是默认值,如果是默认值,那么使用当前创建ComponentScanAnnotationParser对象时指定的beanName生成器,
//也就是ConfigurationClassPostProcessor类中的componentScanBeanNameGenerator,即AnnotationBeanNameGenerator
//否则反射调用无参构造器,初始化指定类型的beanName生成器实例
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
//生成代理对象的模式,通常在web应用中使用
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
//设置指定的模式
scanner.setScopedProxyMode(scopedProxyMode);
} else {
//自动选择JDK代理或者CGLib代理
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
//控制符合组件检测条件的类文件,Spring推荐使用includeFilters和excludeFilters
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
//遍历每一个Filter
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
//解析每一个Filter成为TypeFilter类型过滤器集合,继续遍历该集合
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
//设置到includeFilters属性中
scanner.addIncludeFilter(typeFilter);
}
}
//遍历每一个Filter
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
//解析每一个Filter成为TypeFilter类型过滤器集合,继续遍历该集合
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
//设置到excludeFilters属性中
scanner.addExcludeFilter(typeFilter);
}
}
//获取lazyInit加载值,表示是否延迟初始化,默认false
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
//设置给beanDefinitionDefaults属性,这是设置一个默认值属性
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
/*
* 扫描包
*/
/*
* 解析后的包路径字符串
* Spring将会对其解析、扫描其中的bean定义
*/
Set<String> basePackages = new LinkedHashSet<>();
/*
* 获取basePackages属性数组,也就是传递的包路径字符串,默认是个空数组
*/
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
/*
* 通过环境变量解析传递的路径字符串中的占位符,随后根据分隔符分割为一个路径字符串数组
* 支持以","、";"、" "、"\t"、"\n"中的任意字符作为分隔符来表示传递了多个包路径
*/
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
/*
* 获取basePackageClasses属性数组,默认是个空数组
* basePackageClasses属性可以指定了一批类的class,Spring将解析class所在的包路径作为扫描路径
*/
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
//解析class的packageName,存入basePackages包路径字符串中
basePackages.add(ClassUtils.getPackageName(clazz));
}
/*
* 如果解析后的basePackages包数组为空,即没有手动传递包路经
*/
if (basePackages.isEmpty()) {
//那么将当前@ComponentScan注解所在的类所属的包路径作为扫描的包路径
//也就是,默认扫描当前@ComponentScan注解所在的包下面的所有bean定义
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
/*
* 最后添加一个ExcludeFilter到excludeFilters属性中,表示排除当前的配置类的扫描
*/
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
/*
* 上面都是一些准备的逻辑,doScan方法才是真正执行扫描的逻辑,通过ClassPathBeanDefinitionScanner执行
* 无论是注解配置还是XML的配置,扫描包都是调用的这个方法,我们在此前<context:component-scan/>的注解解析文章中已经讲过了
* 该方法将会扫描并且注册这些指定包路径下的bean定义
*/
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
typeFiltersFor解析类型过滤器
ComponentScanAnnotationParser的typeFiltersFor方法用于解析includeFilters或者excludeFilters属性对象,获取解析出来的类型过滤器集合。
@ComponentScan注解的includeFilters或者excludeFilters属性写法如下:
/**
1. ComponentScanAnnotationParser的方法
2. <p>
3. 解析includeFilters或者excludeFilters属性对象,获取解析出来的类型过滤器集合
4. 5. @param filterAttributes includeFilters或者excludeFilters属性对象
6. @return 解析出来的类型过滤器集合
*/
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
//用于保存解析出来的类型过滤器
List<TypeFilter> typeFilters = new ArrayList<>();
//获取type属性值
FilterType filterType = filterAttributes.getEnum("type");
/*
* 获取classes属性值,遍历
*/
for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
switch (filterType) {
//添加注解类型过滤器
case ANNOTATION:
Assert.isAssignable(Annotation.class, filterClass,
"@ComponentScan ANNOTATION type filter requires an annotation type");
@SuppressWarnings("unchecked")
Class<Annotation> annotationType = (Class<Annotation>) filterClass;
typeFilters.add(new AnnotationTypeFilter(annotationType));
break;
//添加类或者接口的类型过滤器
case ASSIGNABLE_TYPE:
typeFilters.add(new AssignableTypeFilter(filterClass));
break;
//添加自定义类型过滤器
case CUSTOM:
Assert.isAssignable(TypeFilter.class, filterClass,
"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
TypeFilter filter = ParserStrategyUtils.instantiateClass(filterClass, TypeFilter.class,
this.environment, this.resourceLoader, this.registry);
typeFilters.add(filter);
break;
//其他类型的过滤器将抛出异常
default:
throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
}
}
/*
* 获取pattern属性值,遍历
*/
for (String expression : filterAttributes.getStringArray("pattern")) {
switch (filterType) {
//创建一个AspectJTypeFilter,expression应该是一个AspectJ表达式,通过该表达式匹配类或者接口(及其子类、子接口)
case ASPECTJ:
typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
break;
//创建一个RegexPatternTypeFilter,expression应该是一个正则表达式,通过该表达式匹配类或者接口(及其子类、子接口)
case REGEX:
typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
break;
//其他类型的过滤器将抛出异常
default:
throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
}
}
//返回解析出来的类型过滤器集合
return typeFilters;
}
processImports处理@Import注解
processImports方法用于处理配置类上的@Import注解。@Import注解可以传递一个class的数组,用于引入bean定义。在处理上,针对传递的class分为三种类型分类处理:
实现了ImportSelector接口的class,该class对应的类本身不会被注册为bean定义,但是它的selectImports方法返回的类路径数组中的类将可能会被注册为bean定义。但是在该方法中仅仅是递归调用processImports方法,对于返回的类路径数组中的符合条件的类继续解析,直到它是一个普通类或者ImportBeanDefinitionRegistrar类型。
实现了ImportBeanDefinitionRegistrar接口的class,该class对应的类本身不会被注册为bean定义,但是它的registerBeanDefinitions方法可用于自定义注册bean定义。但是在该方法中仅仅是加入importBeanDefinitionRegistrars缓存中,后续通过reader.loadBeanDefinitions 方法统一处理。
普通类型的class,该class对应的类将尝试被注册为bean定义。但是在该方法中仅仅是将其当作配置类递归调用processConfigurationClass方法处理,并且会注册到configurationClasses缓存中,后续通过reader.loadBeanDefinitions 方法统一处理。
还会将被引入普通class的的全路径名字符串和引入该bean类的AnnotationMetadata元数据通过registerImport方法存入importStack栈的imports缓存中,这有这里会调用该方法,后面的ImportAwareBeanPostProcessor.postProcessBeforeInitialization方法就可能用到这里的存入的数据。
因此通过@Import注解引入的bean定义不会在该方法中注册,而是在后续通过reader.loadBeanDefinitions 方法统一注册。
/**
* ConfigurationClassParser的方法
* <p>
* 处理@Import注解
*
* @param configClass 表示当前配置类的对象
* @param currentSourceClass 源类
* @param importCandidates @Import注解引入的类的SourceClass集合
* @param exclusionFilter 类型过滤器
* @param checkForCircularImports 是否校验循环import依赖
*/
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
//如果没有引入任何类,那么直接返回
if (importCandidates.isEmpty()) {
return;
}
//检测是否有循环import的情况
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
} else {
//当前配置类configClass入栈
this.importStack.push(configClass);
try {
//遍历sourceClass集合
for (SourceClass candidate : importCandidates) {
//如果Import的类实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//反射创建一个ImportSelector对象
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);
}
//如果selector还属于DeferredImportSelector接口,那么将在最后执行
if (selector instanceof DeferredImportSelector) {
//存入ImportSelector的延迟处理器中,后面会统一在外部parse方法末尾通过this.deferredImportSelectorHandler.process()延迟处理
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
//获取当前ImportSelector的selectImports方法返回值,也就是要导入到容器中的组件全类名数组,可以指定多个
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//遍历要导入到容器中的组件全类名数组,排除符合exclusionFilter条件的组件类名,对于剩下的组件全类名进行加载成为SourceClass集合
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
/*
* 递归调用processImports方法,对于从当前import类中解析出来的importSourceClasses继续解析
*/
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
//如果Import的类实现了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
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//当前registrar和importingClassMetadata存入configClass的importBeanDefinitionRegistrars缓存中,后续才会处理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
//如果Import的类是普通类型
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
//存入importStack栈的imports缓存中
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//将其当作配置类递归调用processConfigurationClass方法处理,并且会注册到configurationClasses缓存中
//后续通过reader.loadBeanDefinitions 方法统一处理
//这里的asConfigClass方法将当前外部配置类的设置到Import类的importedBy集合中,表示算作被外部类Import引入进来的
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 {
//当前配置类configClass出栈
this.importStack.pop();
}
}
}
asSourceClasses获取符合条件的SourceClass集合
该方法将从selectImports的返回值数组中继续筛选获取不匹配exclusionFilter排除过滤器的className创建SourceClass,并返回一个SourceClass集合。
默认的exclusionFilter就DEFAULT_EXCLUSION_FILTER,该filter的条件是如果类名以"java.lang.annotation."或者"org.springframework.stereotype."开头,就返回true,或者返回false。实际含义就是如果是这些注解类型,那就丢弃。
/**
* ConfigurationClassParser的方法
* <p>
* 获取符合filter条件的类名的SourceClass集合
*/
private Collection<SourceClass> asSourceClasses(String[] classNames, Predicate<String> filter) throws IOException {
List<SourceClass> annotatedClasses = new ArrayList<>(classNames.length);
for (String className : classNames) {
annotatedClasses.add(asSourceClass(className, filter));
}
//返回SourceClass集合
return annotatedClasses;
}
/**
* ConfigurationClassParser的属性
* 替代被排除的className的SourceClass对象
*/
private final SourceClass objectSourceClass = new SourceClass(Object.class);
/**
* ConfigurationClassParser的方法
* <p>
* 获取符合filter条件的类名的SourceClass
*/
SourceClass asSourceClass(@Nullable String className, Predicate<String> filter) throws IOException {
//如果符合过滤条件,那么返回objectSourceClass常量,后续将不会注入该类型的bean实例
//注意这里还是返回了一个objectSourceClass,而不是null,后面会处理
if (className == null || filter.test(className)) {
return this.objectSourceClass;
}
//如果class名以java开头,这表示位于rt.jar核心包中的核心类
if (className.startsWith("java")) {
// Never use ASM for core java types
try {
//直接传递生成的class对象
return new SourceClass(ClassUtils.forName(className, this.resourceLoader.getClassLoader()));
} catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to load class [" + className + "]", ex);
}
}
//根据className,返回SourceClass
return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
}
@Import注解使用
我们测试三种class类型,位于com.spring.source.imports包下:
/**
* @author lx
*/
public class BeanTest {
/**
* 普通class
*/
public static class NormalBean {}
/**
* 实现了ImportSelector的class
*/
public static class ImportSelectorBean implements ImportSelector {
/**
* 返回的类路径字符串数组,将会注册这些类型的bean定义
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String[] strings = new String[2];
strings[0] = "com.spring.source.imports.BeanTest.ImportSelectorBean1";
strings[1] = "com.spring.source.imports.BeanTest.ImportSelectorBean2";
return strings;
}
}
/**
* 实现了ImportBeanDefinitionRegistrar的class
*/
public static class ImportBeanDefinitionRegistrarBean implements ImportBeanDefinitionRegistrar {
/**
* 可以自定义注册bean定义的逻辑
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
//创建bean定义
RootBeanDefinition beanDefinition1 = new RootBeanDefinition();
beanDefinition1.setBeanClass(Object.class);
RootBeanDefinition beanDefinition2 = new RootBeanDefinition();
beanDefinition2.setBeanClass(Object.class);
//注册bean定义
registry.registerBeanDefinition("o1", beanDefinition1);
registry.registerBeanDefinition("o2", beanDefinition2);
}
}
public static class ImportSelectorBean1 {}
public static class ImportSelectorBean2 {}
}
我们的配置类com.spring.source.imports.ImportBean:
@Configuration
@Import({BeanTest.NormalBean.class, BeanTest.ImportSelectorBean.class,
BeanTest.ImportBeanDefinitionRegistrarBean.class})
public class ImportBean { }
配置文件spring-config-import.xml:
<context:component-scan base-package="com.spring.source.imports"/>
测试:
@Test
public void importTest() {
//设置环境变量
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-config-import.xml");
//获取普通bean实例
System.out.println(ac.getBean(BeanTest.NormalBean.class));
//获取通过ImportSelector引入的bean实例
System.out.println(ac.getBean(BeanTest.ImportSelectorBean1.class));
System.out.println(ac.getBean(BeanTest.ImportSelectorBean2.class));
//获取通过ImportBeanDefinitionRegistrarBean手动注册的bean实例
System.out.println(ac.getBean("o1"));
System.out.println(ac.getBean("o2"));
//获取ImportSelector实例本身,获取不到,将会报错
System.out.println(ac.getBean(BeanTest.ImportSelectorBean.class));
//获取ImportBeanDefinitionRegistrarBean实例本身,获取不到,将会报错
System.out.println(ac.getBean(BeanTest.ImportBeanDefinitionRegistrarBean.class));
}
一次测试的结果如下:
com.spring.source.imports.BeanTest$NormalBean@5119fb47
com.spring.source.imports.BeanTest$ImportSelectorBean1@7193666c
com.spring.source.imports.BeanTest$ImportSelectorBean2@20deea7f
java.lang.Object@3835c46
java.lang.Object@1dde4cb2
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.spring.source.imports.BeanTest$ImportSelectorBean' available
可以看到:
对于普通class,将会把该class本身注入到容器中;
对于ImportSelectorBean的class,将会把selectImports方法返回的一批class注入到容器中。该class本身不会注入到容器中。
源码中还要求排除匹配exclusionFilter过滤器的类名。默认的filter就DEFAULT_EXCLUSION_FILTER,该filter的条件是如果类名以"java.lang.annotation."或者"org.springframework.stereotype."开头,就返回true,或者返回false。
对于ImportBeanDefinitionRegistrarBean的class,可以通过registerBeanDefinitions方法自定义的注册一批bean定义,后面将会将实例化。该class本身不会注入到容器中。
/**
* 普通class
*/
public static class NormalBean {
}
/**
* 实现了ImportSelector的class
*/
public static class ImportSelectorBean implements ImportSelector {
/**
* 返回的类路径字符串数组,将会注册这些类型的bean定义
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String[] strings = new String[2];
strings[0] = "com.spring.source.imports.BeanTest.ImportSelectorBean1";
strings[1] = "com.spring.source.imports.BeanTest.ImportSelectorBean2";
return strings;
}
@Override
public Predicate<String> getExclusionFilter() {
return "com.spring.source.imports.BeanTest.ImportSelectorBean1"::equals;
}
}
这个过滤器的意思就是不注册ImportSelectorBean1的bean,我们再次测试,发现更早的抛出异常,因为容器中没有注册ImportSelectorBean1类型的bean实例:
com.spring.source.imports.BeanTest$NormalBean@7193666c
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.spring.source.imports.BeanTest$ImportSelectorBean1' available
parser.validate校验配置类
在通过parser.parse方法解析每一个配置类之后,随即调用parser.validate()方法校验所有解析之后的配置类,也就是ConfigurationClass。
如果当前配置类具有@Configuration注解,并且proxyBeanMethods属性为true,默认就是true:
如果当前配置类是被final修饰的最终类,那么抛出异常:"@Configuration class ‘%s’ may not be final. Remove the final modifier to continue.",因为它不能被CGLIB代理。
如果@Bean注解标注的方法是final或者private且非static修饰的方法,那么抛出异常:"@Bean method ‘%s’ must not be private or final; change the method’s modifiers to continue",因为它不能被CGLiB代理。注意@Bean方法可以被static修饰。
/**
* ConfigurationClassParser的方法
* <p>
* 校验全部ConfigurationClass
*/
public void validate() {
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
//校验每一个configClass
configClass.validate(this.problemReporter);
}
}
/**
* ConfigurationClass的方法
*
* @param problemReporter 问题报告器
*/
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
//如果当前配置类具有@Configuration注解,并且proxyBeanMethods属性为true(默认就是true)
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
//如果当前配置类是被final修饰的最终类,那么抛出异常:"@Configuration class '%s' may not be final. Remove the final modifier to continue."
//因为它不能被CGLIB代理
if (this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
//校验全部@Bean注解方法
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
}
/**
* BeanMethod的方法
* 校验@Bean注解标注的方法
*/
@Override
public void validate(ProblemReporter problemReporter) {
//如果是静态方法,不需要校验,直接返回
if (getMetadata().isStatic()) {
// static @Bean methods have no constraints to validate -> return immediately
return;
}
//如果当前方法所属配置类具有@Configuration注解
if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
//如果当前方法不能被重写,那么抛出异常:"@Bean method '%s' must not be private or final; change the method's modifiers to continue"
//因为只有可重写的方法才能被CGLiB代理
//如果是final、static、private修饰的方法,那么isOverridable方法就返回true
if (!getMetadata().isOverridable()) {
// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
problemReporter.error(new NonOverridableMethodError());
}
}
}
new ConfigurationClassBeanDefinitionReader 创建bean定义加载器
ConfigurationClassPostProcessor将使用ConfigurationClassBeanDefinitionReader来加载、注册此前通过parse方法找到的可能存在的bean定义。
//----------ConfigurationClassBeanDefinitionReader的相关属性
private final BeanDefinitionRegistry registry;
private final SourceExtractor sourceExtractor;
private final ResourceLoader resourceLoader;
private final Environment environment;
private final BeanNameGenerator importBeanNameGenerator;
private final ImportRegistry importRegistry;
private final ConditionEvaluator conditionEvaluator;
/**
* ConfigurationClassBeanDefinitionReader构造器
*
* @param registry bean定义要存入的注册表
* @param sourceExtractor 简单的策略,允许工具控制源元数据如何附加到 bean 定义元数据。
* @param resourceLoader 资源加载器
* @param environment 环境变量
* @param importBeanNameGenerator import方式引入的bean的beanName生成器
* @param importRegistry import方式引入的bean注册表
*/
ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator,
ImportRegistry importRegistry) {
//初始化一批属性
this.registry = registry;
this.sourceExtractor = sourceExtractor;
this.resourceLoader = resourceLoader;
this.environment = environment;
this.importBeanNameGenerator = importBeanNameGenerator;
this.importRegistry = importRegistry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
reader.loadBeanDefinitions 加载bean定义
通过reader加载、注册所有configClasses中的bean定义,也就是对configClasses中的@Import、@Bean、@ImportResource等注解中可能的bean定义进行解析和注册。
该方法结束,那么processConfigBeanDefinitions方法的主要逻辑就结束了。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 向注册表注册 bean 定义
*
* @param configurationModel 解析后的ConfigurationClass配置类集合
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
//评估@Conditional注释,判断当前配置类的解析否需要跳过,将会跟踪结果并考虑到"importBy"。
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//遍历解析后的ConfigurationClass配置类集合,加载bean定义
for (ConfigurationClass configClass : configurationModel) {
//从ConfigurationClass加载bean定义
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
reader.loadBeanDefinitions 加载bean定义
通过reader加载、注册所有configClasses中的bean定义,也就是对configClasses中的@Import、@Bean、@ImportResource等注解中可能的bean定义进行解析和注册。
该方法结束,那么processConfigBeanDefinitions方法的主要逻辑就结束了。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 向注册表注册 bean 定义
*
* @param configurationModel 解析后的ConfigurationClass配置类集合
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
//评估@Conditional注释,判断当前配置类的解析否需要跳过,将会跟踪结果并考虑到"importBy"。
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//遍历解析后的ConfigurationClass配置类集合,加载bean定义
for (ConfigurationClass configClass : configurationModel) {
//从ConfigurationClass加载bean定义
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
new TrackedConditionEvaluator 跟踪条件评估器
ConfigurationClassBeanDefinitionReader的内部类,同样是用于判断当前配置类的解析否需要跳过,和之前的Evaluator区别是除了评估@Conditional注解之外,还会跟踪结果并考虑到"importedBy"。
它的shouldSkip方法很简单,如果当前configClass是被引入的,并且引入类都是被跳过的,那么当前configClass也应该被跳过,否则通过原始的conditionEvaluator.shouldSkip方法判断是否需要跳过,这里的phase生效的阶段参数为REGISTER_BEAN,即注册bean的阶段。
解析后的结果将会存入skipped缓存,后续再来查询时将不会再次解析。
/**
1. ConfigurationClassBeanDefinitionReader的内部类
2. 评估@Conditional注解,跟踪结果并考虑到"importedBy"
*/
private class TrackedConditionEvaluator {
/**
* ConfigurationClass的缓存,避免后续重复校验应该跳过解析
*/
private final Map<ConfigurationClass, Boolean> skipped = new HashMap<>();
/**
* 是否应该跳过解析
*
* @param configClass
* @return
*/
public boolean shouldSkip(ConfigurationClass configClass) {
//从缓存获取当前configClass的是否应该跳过的结果
Boolean skip = this.skipped.get(configClass);
//如果缓存为null,那么解析
if (skip == null) {
//如果是被其他类引入的
if (configClass.isImported()) {
//所有的引入类被跳过的标记,默认true
boolean allSkipped = true;
//获取引入类
for (ConfigurationClass importedBy : configClass.getImportedBy()) {
//如果引入类不应该被跳过
if (!shouldSkip(importedBy)) {
//那么allSkipped为false,结束循环
allSkipped = false;
break;
}
}
//如果所有的引入类被跳过,那么skip设置为true,表示当前被引入的类应该被跳过
if (allSkipped) {
// The config classes that imported this one were all skipped, therefore we are skipped...
skip = true;
}
}
//如果skip还是为null
if (skip == null) {
//那么调用conditionEvaluator的shouldSkip方法继续判断,这里的phase生效的阶段参数为REGISTER_BEAN,即注册bean的阶段
//这个方法我们此前就讲过了
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
}
//存入缓存
this.skipped.put(configClass, skip);
}
//返回skip
return skip;
}
}
loadBeanDefinitionsForConfigurationClass加载注册beean定义
通过reader加载、注册此configClasses中的bean定义,主要有一下4个加载的逻辑:
如果当前配置类是被引入的,通过@Import注解、处理内部类等方式找到的configClass,都算作被引入的。那么解析注册配置类本身成为AnnotatedGenericBeanDefinition类型的bean定义,并且注册到注册表中。在这里,就会对非静态内部配置类进行注册。
解析@Bean方法成为ConfigurationClassBeanDefinition类型的bean定义,并且注册到注册表中。
加载、解析@ImportedResource注解引入的XML配置文件中的bean定义到注册表中。核心还是BeanDefinitionReader.loadBeanDefinitions方法加载resource资源,解析、注册bean定义,
对于@Import注解引入的ImportBeanDefinitionRegistrar类型的对象的registerBeanDefinitions方法进行统一回调。该方法可用于自定义的注册、修改bean定义,因为它将注册表作为参数。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 读取特定的ConfigurationClass配置类,注册该类本身及其所有@Bean方法的 bean 定义
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//如果trackedConditionEvaluator的shouldSkip方法返回true,即应该跳过
if (trackedConditionEvaluator.shouldSkip(configClass)) {
//获取beanName
String beanName = configClass.getBeanName();
//如果存在beanName,并且注册表中已经包含了该beanName的bean定义
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
/*
* 从注册表中移除该beanName的bean定义
* 因此,此前加入进来的配置类的bean定义将可能被移除
*/
this.registry.removeBeanDefinition(beanName);
}
//移除importRegistry即importStack中的缓存
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//如果当前配置类是被引入的,通过@Import注解、处理内部类等方式找到的configClass,都算作被引入的
if (configClass.isImported()) {
/*
* 1 解析注册配置类本身成为AnnotatedGenericBeanDefinition类型的bean定义,并且注册到注册表中。
* 在这里,就会对非静态内部配置类进行注册。
*/
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//获取全部的@Bean方法,遍历
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
/*
* 2 解析@Bean方法成为ConfigurationClassBeanDefinition类型的bean定义,并且注册到注册表中。
*/
loadBeanDefinitionsForBeanMethod(beanMethod);
}
/*
* 3 加载、解析@ImportedResource注解引入的XML配置文件中的bean定义到注册表中。
* 核心还是BeanDefinitionReader的loadBeanDefinitions方法加载resource资源,解析、注册bean定义
* 这个方法我们在"IoC容器初始化(2)"的文章中就详细讲过了
*/
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
/*
* 4 @Import注解引入的ImportBeanDefinitionRegistrar类型的对象的registerBeanDefinitions方法的回调。
* 该方法可用于自定义的注册、修改bean定义,因为它将注册表作为参数
*/
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
registerBeanDefinitionForImportedConfigurationClass注册配置类本身
如果当前配置类是被引入的,此前讲过,通过@Import注解、处理内部类等方式找到的configClass,都算作被引入的,那么该方法将配置类本身注册为 bean 定义,这里就是对@Import普通类和非静态内部类的beean定义进行注册的地方。
将被解析为AnnotatedGenericBeanDefinition类型的bean定义,采用的beanName生成器是FullyQualifiedAnnotationBeanNameGenerator,在没找到beanName而主动生成的时候,生成的逻辑仅仅是将了类的全路径名作为beanName,相比于父类AnnotationBeanNameGenerator更加简单。
注册的bean定义是和XML标签的解析一样是采用registerBeanDefinition方法,就是向注册表中添加的三个缓存beanDefinitionMap、beanDefinitionNames、aliasMap,这属于容器初始化的核心操作。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 将配置类本身注册为 bean 定义,这里就是对非静态内部配置类进行注册的地方
*/
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
//创建AnnotatedGenericBeanDefinition类型的bean定义
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
//查找或者生成beanName,采用的生成器是FullyQualifiedAnnotationBeanNameGenerator
//它继承了AnnotationBeanNameGenerator,区别就在于如果没指定beanName那么自己的beanName生成规则是直接以全路径类名作为beanName
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
//处理类上的其他通用注解:@Lazy, @Primary, @DependsOn, @Role, @Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
//封装成为BeanDefinitionHolder对象
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
//根据proxyMode属性的值,判断是否需要创建scope代理,一般都是不需要的
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//调用registerBeanDefinition方法注册BeanDefinition到注册表的缓存中,该方法此前已经讲过了
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
//设置beanName
configClass.setBeanName(configBeanName);
if (logger.isTraceEnabled()) {
logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
}
}
/**
* FullyQualifiedAnnotationBeanNameGenerator类,用于查找和生成beanName
* 重写了Spring生成beanName0的逻辑
*/
public class FullyQualifiedAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
/**
* 重写了Spring生成beanName0的逻辑
* 相比于父类AnnotationBeanNameGenerator更加简单,直接是将全路径类名注解作为beanName
*/
@Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
return beanClassName;
}
}
loadBeanDefinitionsForBeanMethod从@Bean方法加载bean定义
@Bean方法对应的bean定义将被封装成为ConfigurationClassBeanDefinition类型,如果设置了name属性,那么将第一个值作为beanName,其他的值作为别名,否则直接将方法名作为beanName。
如果在注册当前@Bean方法的bean定义时发现注册表中存在同名的的bean定义,那么可能会覆盖此前的bean定义或者保留此前的bean定义而不注册当前bean定义(具体在isOverriddenByExistingDefinition方法中)。
@Bean方法的bean定义的注册和XML标签的解析一样是采用registerBeanDefinition方法,就是向注册表中添加的三个缓存beanDefinitionMap、beanDefinitionNames、aliasMap,这属于容器初始化的核心操作。
可以看到,@Bean方法对应的ConfigurationClassBeanDefinition,设置了FactoryMethodName属性,也就是说,这里的@Bean方法均被解析为工厂方法,那么在IoC容器初始化的createBeanInstance创建bean实例阶段,将会通过工厂方法创建bean实例,即调用instantiateUsingFactoryMethod方法获取实例。
/**
* ConfigurationClassBeanDefinitionReader的属性
* 条件评估器,处理类或者方法上的@Conditional注解
*/
private final ConditionEvaluator conditionEvaluator;
/**
* ConfigurationClass的属性
* 当前配置类跳过的方法
*/
final Set<String> skippedBeanMethods = new HashSet<>();
/**
1. ConfigurationClassBeanDefinitionReader的方法
2. <p>
3. 根据给定的BeanMethod解析为bean定义像注册表中注册
4. 5. @param beanMethod 表示一个@Bean方法
*/
@SuppressWarnings("deprecation") // for RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
//获取方法所属的类
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
//获取方法名
String methodName = metadata.getMethodName();
/*
* 处理方法上的@Conditional注解,判断是否应该跳过此方法的处理
* 这里的metadata就是方法元数据,MethodMetadata
*/
//如果shouldSkip返回true,即当前@Bean的方法应该跳过解析,这里的phase生效的阶段参数为REGISTER_BEAN
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
//那么加入到当前配置类的skippedBeanMethods缓存中
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");
/*
* 考虑名字和别名
*/
//获取name属性集合
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
/*
* 获取beanName。如果设置了name属性,那么将第一个值作为beanName,其他的值作为别名,否则直接将方法名作为beanName
*/
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
for (String alias : names) {
/*
* 注册别名映射
* 将是将别名alias和名字beanName的映射注册到SimpleAliasRegistry注册表的aliasMap缓存汇总
*/
this.registry.registerAlias(beanName, alias);
}
/*
* 校验是否存在同名的bean定义,以及是否允许同名的bean定义覆盖
*/
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
//如果返回true,并且如果beanName就等于当前bean方法所属的类的beanName,那么抛出异常
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
//如果返回true,直接返回,当前bean方法不再解析
return;
}
//新建一个ConfigurationClassBeanDefinition类型的bean定义,从这里可知@Bean方法的bean定义的类型
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
//如果当前bean方法是静态的
if (metadata.isStatic()) {
//看作静态工厂方法
// static @Bean method
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
} else {
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
}
//设置工厂方法名
beanDef.setUniqueFactoryMethodName(methodName);
} else {
//看作实例工厂方法
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
//设置解析的工厂方法
if (metadata instanceof StandardMethodMetadata) {
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
}
//设置自动装配模式为构造器自动注入
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
//设置属性
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
//处理方法上的其他通用注解:@Lazy, @Primary, @DependsOn, @Role, @Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
//获取autowire属性,默认Autowire.NO,即不自动注入
Autowire autowire = bean.getEnum("autowire");
//设置自动注入模式
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
//设置autowireCandidate属性,默认tue
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
//设置initMethodName属性
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
//设置destroyMethod属性
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
//考虑作用域和代理
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
//设置作用域
beanDef.setScope(attributes.getString("value"));
//获取作用域代理属性,默认不使用代理
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
//如有必要,将原始 bean 定义替换为代理目标定义
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
//调用registerBeanDefinition方法注册BeanDefinition到注册表的缓存中,该方法此前已经讲过了
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
isOverriddenByExistingDefinition是否支持现有bean定义的覆盖
使用获取或者生成的beanName,在注册表中查找同名的bean定义并校验,对于@Bean的bean定义来说可能会覆盖其他同名bean定义或者被其他同名bean定义:
如果注册表中不包含该beanName的bean定义,那么返回false,可以继续向后解析该bean方法。后面的步骤都表示注册表中包含该beanName的bean定义;
如果现有的 bean 定义也是从通过@Bean方法创建的,那么允许bean 方法重写:
如果这两个@Bean方法不属于同一个类。那么返回false,当前@Bean方法将覆盖这个同名bean定义。
如果这两个@Bean方法属于同一个类,并且这两个@Bean方法的方法名一致,即存在重载方法情况,那么返回true,将保留现有的bean定义,不再继续向后解析该@Bean方法。
从Spring 4.2开始,由组件扫描(基于组件注解)产生的bean定义可以被@Bean方法静默地覆盖:
如果该同名bean定义是通过组件扫描注解产生的(通过组件扫描注解产生的bean定义类型就是ScannedGenericBeanDefinition)。那么返回false,当前bean方法将覆盖这个同名bean定义。
现有 bean 定义的role已标记为框架生成的 bean 吗?如果是,这允许当前 bean 方法重写它,因为它是应用程序级,返回false,当前bean方法将覆盖这个同名bean定义。
如果现有 bean 定义是通过XML产生的,并且不允许同名的BeanDefinition 覆盖(默认允许)那么因为存在同名的bean定义而抛出异常"@Bean definition illegally overridden by existing bean definition: "。
最后的返回true,表示不存在同名的bean定义或者允许同名bean定义覆盖。
/**
* ConfigurationClassBeanDefinitionReader的方法
*
* 是否已存在同名bean定义或者允许现有的bean定义被覆盖
*/
protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
//如果注册表中不包含该beanName的bean定义
if (!this.registry.containsBeanDefinition(beanName)) {
//直接返回false
return false;
}
//到这里,表示注册表中包含该beanName的bean定义
BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
/*
* 如果现有的 bean 定义也是从通过bean方法创建的,那么允许bean 方法重写
*/
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
//如果这两个bean方法属于同一个类
if (ccbd.getMetadata().getClassName().equals(
beanMethod.getConfigurationClass().getMetadata().getClassName())) {
//如果这两个bean方法的方法名一致,即重载方法情况,那么返回true,保留现有的 bean 定义
if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
}
return true;
}
//否则,返回false,因为这两个bean方法不属于同一个类,当前bean方法将覆盖这个同名bean定义
else {
return false;
}
}
/*
* 从Spring 4.2开始,由组件扫描产生的bean定义可以被@Bean方法静默地覆盖
* 如果该同名bean定义是通过组件扫描注解产生的(通过组件扫描主角儿产生的bean定义类型就是ScannedGenericBeanDefinition)
* 那么返回false,当前bean方法将覆盖这个同名bean定义
*/
if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
//返回false,当前bean方法将覆盖这个同名bean定义
return false;
}
/*
* 现有 bean 定义的role已标记为框架生成的 bean 吗?如果是,这允许当前 bean 方法重写它,因为它是应用程序级
* 那么返回false,当前bean方法将覆盖这个同名bean定义
*/
if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
return false;
}
/*
* 如果现有 bean 定义是通过XML产生的,并且不允许同名的BeanDefinition 覆盖(默认允许)
* 那么因为存在同名的bean定义而抛出异常"@Bean definition illegally overridden by existing bean definition: "
*/
if (this.registry instanceof DefaultListableBeanFactory &&
!((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
"already exists. This top-level bean definition is considered as an override.",
beanMethod, beanName));
}
//返回true
return true;
}
loadBeanDefinitionsFromImportedResources从@ImportedResource加载bean定义
加载、解析@ImportedResource注解引入的XML配置文件中的bean定义到注册表中。其内部最终还是调用BeanDefinitionReader的loadBeanDefinitions方法,加载resource资源,解析、注册bean定义。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 加载、解析@ImportedResource注解引入的XML配置文件中的bean定义到注册表中。
*
* @param importedResources 解析后的资源路径字符串
*/
private void loadBeanDefinitionsFromImportedResources(
Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
//bean定义读取器的缓存
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
/*循环加载*/
importedResources.forEach((resource, readerClass) -> {
// Default reader selection necessary?
if (BeanDefinitionReader.class == readerClass) {
//支持 Groovy 语言
if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
// When clearly asking for Groovy, that's what they'll get...
readerClass = GroovyBeanDefinitionReader.class;
} else {
// Primarily ".xml" files but for any other extension as well
readerClass = XmlBeanDefinitionReader.class;
}
}
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
//如果reader为null,那么新建reader
if (reader == null) {
try {
// Instantiate the specified BeanDefinitionReader
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
if (reader instanceof AbstractBeanDefinitionReader) {
AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
abdr.setResourceLoader(this.resourceLoader);
abdr.setEnvironment(this.environment);
}
readerInstanceCache.put(readerClass, reader);
} catch (Throwable ex) {
throw new IllegalStateException(
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
}
}
//最终调用reader的loadBeanDefinitions方法加载resource资源,解析、注册bean定义
reader.loadBeanDefinitions(resource);
});
}
loadBeanDefinitionsFromRegistrars从@Import加载bean定义
该方法主要就是对于此前在processImports方法中的ImportBeanDefinitionRegistrar类型的引入类对象的registerBeanDefinitions方法的统一回调。
/**
* ConfigurationClassBeanDefinitionReader的方法
* <p>
* 注册@Import注解引入的bean定义到注册表中
*
* @param registrars importBeanDefinitionRegistrars缓存
*/
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
//循环importBeanDefinitionRegistrars缓存map,回调每一个ImportBeanDefinitionRegistrar对象的三个参数的registerBeanDefinitions方法
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
postProcessBeanFactory
postProcessBeanFactory方法同样在IoC容器初始化的invokeBeanFactoryPostProcessors步骤中被自动回调,当然调用时机晚于postProcessBeanDefinitionRegistry,并且逻辑也更加简单。
在此前我们已经解析了全部的配置类,该方法就是对于被@Configuration及其派生注解标注的配置类生成CGLIB代理对象,主要目的是对@Bean方法进行代理增强,最后还会添加一个ImportAwareBeanPostProcessor类型的后处理器。
/**
* ConfigurationClassPostProcessor的方法
* <p>
* 对于被@Configuration注解标注的配置类生成CGLIB代理对象,主要目的是在对bean方法的调用进行代理增强
* 最后还会添加一个ImportAwareBeanPostProcessor类型的后处理器
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//一致性哈希值的校验,类似于postProcessBeanDefinitionRegistry方法
int factoryId = System.identityHashCode(beanFactory);
//如果factoriesPostProcessed缓存包含了该factoryId值,那么说明此前该beanFactory已被该ConfigurationClassPostProcessor处理过了
//Spring不允许同一个ConfigurationClassPostProcessor重复处理同一个beanFactory,直接抛出异常
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
//当前beanFactory的factoryId加入factoriesPostProcessed缓存
this.factoriesPostProcessed.add(factoryId);
//如果registriesPostProcessed缓存不包含该factoryId,那么先调用processConfigBeanDefinitions处理所有的配置类
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
/*
* 1 增强配置类
*/
enhanceConfigurationClasses(beanFactory);
/*
* 2 添加ImportAwareBeanPostProcessor类型的BeanPostProcessor后处理器
*/
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
enhanceConfigurationClasses增强配置类
查找全部被@Configuration注解及其派生注解标注的proxyBeanMethods属性的值为true(默认就是true)的配置类,也就是具有"full"属性的配置类,并通过ConfigurationClassEnhancer进行代理增强。
postProcessBeanDefinitionRegistry方法的checkConfigurationClassCandidate方法用于检测配置类并设置相应的属性,被@Configuration注解以及派生注解标注的配置类,就会被标记为"full",其他配置类则标记为“lite”。在该方法中终于用到了这个属性。
对于full模式的Configuration,将会通过ConfigurationClassEnhancer配置类增强器生成CGLIB子类,并设置给当前bean定义的beanClass属性,那么后面在创建该配置类的实例时就会创建代理类的实例!
创建代理子类的核心方法是enhancer.enhance方法!
public static final String CONFIGURATION_CLASS_ATTRIBUTE = Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
/**
* ConfigurationClassPostProcessor的方法
* <p>
* 查找全部被@Configuration注解标注的配置类,也就是具有"full"属性的配置类,并通过ConfigurationClassEnhancer进行代理增强
*/
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
//需要进行代理增强的配置类集合
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
//遍历全部的BeanDefinition
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//获取CONFIGURATION_CLASS_ATTRIBUTE属性的值
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
} catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
//校验是否是full模式的Configuration
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
} else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
//当前full模式的Configuration 的beanName和bean定义都存入configBeanDefs集合
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
//如果configBeanDefs是空集,那么直接返回,不需要增强
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
//新建一个配置类增强器,通过生成CGLIB子类来增强配置类
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
//新设置一个属性,如果@Configuration已代理,那么始终代理目标类
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
//设置用户指定的 bean 类的CGLIB 增强子类
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
//设置class为增强类的class,后续创建该配置类的实例时就会创建代理类的实例
beanDef.setBeanClass(enhancedClass);
}
}
}
enhance增强
被创建的CGLIB子类将继承当前目标配置类,并且实现EnhancedConfiguration接口(EnhancedConfiguration也实现了BeanFactoryAware接口)。
被创建的CGLIB子类包括两个回调过滤器,一个是BeanMethodInterceptor拦截器,将会拦截配置类中的@Bean方法,另一个是BeanFactoryAwareMethodInterceptor拦截器,将会拦截setBeanFactory方法。所谓的“增强”,实际上就是对这两种方法的增强,而增强的具体实现就是通过这两个拦截器实现的!
/**
* ConfigurationClassEnhancer的方法
* <p>
* 加载指定的类并生成其配备容器感知回调的 CGLIB 子类
*
* @return 被增强的子类
*/
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
//如果目标类型已经是EnhancedConfiguration的类型,那么直接返回
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Ignoring request to enhance %s as it has " +
"already been enhanced. This usually indicates that more than one " +
"ConfigurationClassPostProcessor has been registered (e.g. via " +
"<context:annotation-config>). This is harmless, but you may " +
"want check your configuration and remove one CCPP if possible",
configClass.getName()));
}
return configClass;
}
//通过一个Enhancer创建子类class
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
}
return enhancedClass;
}
//---------ConfigurationClassEnhancer的相关属性-------------
/**
* 要使用的回调拦截器
*/
private static final Callback[] CALLBACKS = new Callback[]{
//支持@Bean方法的回调拦截
new BeanMethodInterceptor(),
//支持BeanFactoryAware的setBeanFactory方法的回调拦截
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
/**
* 条件回调筛选器,默认支持@Bean方法的回调拦截和BeanFactoryAware的setBeanFactory方法的回调拦截
*/
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
/**
* ConfigurationClassEnhancer的方法
* <p>
* 创建CGLIB增强器实例
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
//设置继承的父类为目标类型
enhancer.setSuperclass(configSuperClass);
//设置实现的接口为EnhancedConfiguration,并且EnhancedConfiguration也实现了BeanFactoryAware接口
enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
enhancer.setUseFactory(false);
//设置类名命名策略
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
//设置用于从此生成器创建字节码的策略
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
//回调过滤器
enhancer.setCallbackFilter(CALLBACK_FILTER);
//设置回调类型,默认BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
BeanMethodInterceptor拦截器
BeanMethodInterceptor该拦截器实现了cglib的MethodInterceptor(区别于Spring的MethodInterceptor),用于拦截当前配置类内部的任何@Bean方法的调用以及@Bean方法之间的调用,以确保正确处理 bean 语义。
简单的说,对于被@Configuration注解及其派生注解标注的proxyBeanMethods属性的值为true(默认就是true)的配置类,内部的单例@Bean方法仅仅会被调用一次,用于创建对象,后续如果存在任何@Bean方法之间的调用,或者外部对象对@Bean方法的调用(这要求该对象交给Spring容器管理),都会是从容器中直接查找已创建的对象返回,不会再调用@Bean方法,能够保证是同一个实例,即都指向IoC内的单例。
当@Bean方法被调用的时候,就会走intercept方法:
调用getBeanFactory方法获取beanFactory,实际上代理类生成了一个"$$beanFactory"属性,用于存放beanFactory。
该方法会默认将方法名作为beanName,,如果指定了@Bean注解的name属性,那么将会取第一个值作为beanName。
检查当前beanName对应的bean实例或者定义存在,并且是FactoryBean类型,第一次调用的时候应该是不存在的。如果存在就返回一个FactoryBean代理对象。
isCurrentlyInvokedFactoryMethod方法用于检查给定方法是否对应于容器当前调用的方法,仅比较方法名称和参数类型。也就是说,如果@Bean方法是Spring自动调用的,比如用于创建对象,那么返回true,如果在其他@Bean方法中被调用,那么返回false。
如果是Spring自动调用的该@Bean方法,那么调用invokeSuper方法,实际上就是调用当前@Bean方法本身,用于创建bean实例,没有任何增强,返回对应的结果。
如果当前@Bean方法是在其他@Bean方法中被调用的。那么调用resolveBeanReference尝试直接从容器中获取给定BeanName的对象,如果容器中有,就直接返回,该@Bean方法后续不再被调用;如果没有,那么创建,该@Bean方法被调用一次,后续不再被调用。其核心就是beanFactory.getBean方法。
/**
* BeanMethodInterceptor的方法
* <p>
* 通过检查提供的 BeanFactory 是否已存在此 Bean 对象,来增强此@Bean方法
*
* @param enhancedConfigInstance 当前代理子类对象
* @param beanMethod 调用方法
* @param beanMethodArgs 方法参数
* @param cglibMethodProxy 方法代理对象
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
//调用getBeanFactory方法获取beanFactory,实际上代理类生成了一个"$$beanFactory"属性,用于存放beanFactory
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
//获取当前@Bean方法的beanName,默认就是方法名,如果指定了@Bean注解的name属性,那么将会取第一个值作为beanName
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
//是否是作用域代理,一般都不是
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
//如果要处理 bean 方法间引用的情况,我们必须显式检查容器中已有缓存的实例。
//检查当前beanName对应的bean定义存在并且是FactoryBean类型,第一次调用的时候应该是不存在的
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
//获取factoryBean实例本身
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
//创建一个FactoryBean的增强类来拦截getObject方法
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
} else {
// 这里将会选择合适的代理方式,JDK的代理或者CGLIB的代理
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
/*
* 检查给定方法是否对应于容器当前调用的方法,仅比较方法名称和参数类型
* 也就是说,如果@Bean方法是Spring自动调用的,比如用于创建对象,那么返回true
* 如果在其他@Bean方法中被调用,那么返回false
*/
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
//调用invokeSuper方法,实际上就是调用当前@Bean方法本身,没有任何增强,返回对应的结果
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
/*
* 到这里,表示当前@Bean方法是在其他@Bean方法中被调用的
* 那么直接从容器中获取给定BeanName的对象,如果容器中有,就直接返回,该@Bean方法后续不再被调用
* 如果没有,那么创建,该@Bean方法被调用一次,后续不再被调用
*/
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
/**
* 检查beanFactory中是否包含指定beanName的bean实例或者bean定义
*/
private boolean factoryContainsBean(ConfigurableBeanFactory beanFactory, String beanName) {
return (beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName));
}
/**
* 检查给定方法是否对应于容器当前调用的方法,仅比较方法名称和参数类型
* 也就是说,如果@Bean方法是Spring自动调用的,比如用于创建对象,那么返回true
* 如果在其他@Bean方法中被调用,那么返回false
*/
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
resolveBeanReference解析bean引用
如果当前@Bean方法是在其他@Bean方法中被调用,或者是外部对象对@Bean方法的调用(这要求该对象交给Spring容器管理),那么直接从容器中获取给定BeanName的对象,如果容器中有,就直接返回,该@Bean方法后续不再被调用。如果没有,那么创建,该@Bean方法被调用一次,后续不再被调用。
核心方法就是IoC容器初始化的finishBeanFactoryInitialization阶段beanFactory.getBean方法(该方法我们在),如果有缓存,就从缓存取,没有就创建。创建的方法,在getBean方法内部的createBeanInstance方法中,将会通过工厂方法创建bean实例,即调用instantiateUsingFactoryMethod方法获取实例。IoC容器初始化我们在前面的文章就讲过了。
如果@Bean方法之间存在互相调用,可以是间接的通过其他外部对象(这要求该对象交给Spring容器管理)调用,那么表示它们存在依赖关系,将会通过registerDependentBean方法注册到容器中,如果仅仅是外部对象(这要求该对象交给Spring容器管理)调用@Bean方法,那么不会注册依赖关系。
/**
* BeanMethodInterceptor的方法
* <p>
* 如果当前@Bean方法是在其他@Bean方法中被调用,或者是外部对象对@Bean方法的调用(这要求该对象交给Spring容器管理)
* 那么直接从容器中获取给定BeanName的对象,如果容器中有,就直接返回,该@Bean方法后续不再被调用
* 如果没有,那么创建,该@Bean方法被调用一次,后续不再被调用
* 核心就是beanFactory.getBean方法
*/
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
//当前bean是否在创建中
boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
try {
if (alreadyInCreation) {
//设置为非创建状态
beanFactory.setCurrentlyInCreation(beanName, false);
}
//判断是否需要使用参数
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
//如果需要使用参数并且当前beanName对应的bean是单例的
if (useArgs && beanFactory.isSingleton(beanName)) {
//如果有一个参数为null,那么useArgs设置为false,@Bean单例对象的参数不是可选的
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
//调用beanFactory.getBean方法获取bean实例,这一步就是从缓存中获取,如果没有,那么就创建,创建的时候就会调用那个该@Bean方法
//如果有就直接返回,不再调用该@Bean方法
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName));
//是否等于给定类型,一般都不相等,除了字符串类型
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
//这里的equals是为了检测NullBean实例
if (beanInstance.equals(null)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] returned null bean; resolving to null value.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName()));
}
//如果是NullBean,那么设置为null
beanInstance = null;
} else {
String msg = String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] but overridden by non-compatible bean instance of type [%s].",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
try {
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
} catch (NoSuchBeanDefinitionException ex) {
// Ignore - simply no detailed message then.
}
throw new IllegalStateException(msg);
}
}
//获取当前最外层正在被调用的@Bean方法,也就是直接或者见解调用该@Bean方法的@Bean方法,因此可能为null
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
if (currentlyInvoked != null) {
//获取外部@Bean方法的beanName
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
//注册外部@Bean方法和内部@Bean方法的依赖关系
beanFactory.registerDependentBean(beanName, outerBeanName);
}
return beanInstance;
} finally {
//重新设置为正在创建中
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}
}
BeanFactoryAwareMethodInterceptor拦截器
BeanFactoryAwareMethodInterceptor拦截器用于拦截当前被@Configuration注解及其派生注解标注的配置类内部的setBeanFactory方法的调用在。
它的intercept方法就很简单了,就是为CGLIB子类对象的"$$beanFactory"属性赋值为当前的beanFactory实例,在后面拦截@Bean方法的时候会用到(前面见识过了)。
/**
* BeanFactoryAwareMethodInterceptor的方法
* <p>
* 拦截BeanFactoryAware的setBeanFactory方法回调,为"$$beanFactory"属性赋值
* 这里的拦截早于@Bean方法的拦截,在创建对象之后就马上调用了该方法
*/
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//获取代理对象的"$$beanFactory"字段
Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated BeanFactory field");
//设置置为第一个参数,也就是注入的beanFactory实例,在后面拦截@Bean方法的时候会用到
field.set(obj, args[0]);
//如果实际代理的目标类型还实现了BeanFactoryAware接口,那么还是调用其对应的setBeanFactory方法,否则直接退出
if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
return proxy.invokeSuper(obj, args);
}
return null;
}
ImportAwareBeanPostProcessor后处理器
在postProcessBeanFactory方法的最后,注册了一个ImportAwareBeanPostProcessor类型的后处理器,这个后处理器就是为了支持配置类代理类对象的创建!
postProcessProperties方法在createBeanInstance方法创建bean实例之后, initializeBean初始化bean之前的populateBean装配bean的方法中被调用,就是用于对于EnhanceConfiguration类型的代理对象调用setBeanFactory方法设置beanFactory的属性值的。
postProcessBeforeInitialization方法则是在initializeBean初始化bean的时候被调用,用于ImportAware类型的bean实例的setImportMetadata方法回调,获取引入当前类的类元数据的最后一个(当前类就是被@Import注解引入的普通类,引入类就是添加@Import注解的类),设置到setImportMetadata方法参数中。
/**
* ConfigurationClassPostProcessor的属性
*/
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
/**
1. ImportAwareBeanPostProcessor类,位于ConfigurationClassPostProcessor内部
*/
private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
private final BeanFactory beanFactory;
public ImportAwareBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* 为EnhancedConfiguration代理对象调用setBeanFactory方法设置beanFactory的属性值
*/
@Override
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
//对于EnhancedConfiguration类型的代理对象注入beanFactory实例
if (bean instanceof EnhancedConfiguration) {
((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
}
return pvs;
}
/**
* 用于ImportAware的setImportMetadata方法回调,设置importMetadata元数据
* 主要是支持@Import注解引入的类
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//如果是ImportAware类型
if (bean instanceof ImportAware) {
//获取IMPORT_REGISTRY_BEAN_NAME的bean实例,在前面的processConfigBeanDefinitions方法最后就注册了该名字的bean实例
ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
//获取引入当前类的类元数据的最后一个,当前类就是被@Import注解引入的普通类,引入类就是添加@Import注解的类
AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
if (importingClass != null) {
//设置到setImportMetadata方法参数中
((ImportAware) bean).setImportMetadata(importingClass);
}
}
return bean;
}
}
ConfigurationClassPostProcessor总结
我们前面学习了ConfigurationClassPostProcessor的两大方法postProcessBeanDefinitionRegistry和postProcessBeanFactory,现在我们终于知道这个后处理器的重要性了吧?现在我们来简单总结一下:
postProcessBeanDefinitionRegistry方法:
先被回调,用于整体解析配置类,注册bean定义。
如果具有@Configuration注解及其派生注解标注并且proxyBeanMethods属性的值为true(默认就是true),那么算作配置类,设置为full模式,如果具有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解及其派生注解之一,那么同样算作配置类,设置为lite模式;这个模式将会在postProcessBeanFactory方法中用到。关于full和lite模式官方也有解释。
解析配置类的内部类以及各种注解:@Order 排序注解、@Conditional条件注解、@PropertySources以及@PropertySource属性源注解、@ComponentScans以及@ComponentScan组件扫描注解、@Import引入bean注解、@ImportResource配置文件注解、@Bean注解。主要目的是解析出其中的bean定义并且注册到容器中,核心方法就是paese方法和loadBeanDefinitions方法。
这里也是对于非静态内部类的bean定义进行注册的地方,具体方法就是registerBeanDefinitionForImportedConfigurationClass,默认beanName就是类的全路径名。此前容器初始化过程中所讲的doScan方法中并没有解析注册非静态内部类的bean定义。
postProcessBeanFactory方法:
后被回调,进一步处理配置类,对full模式配置类进行代理增强配置。
对于具有@Configuration注解及其派生注解标注的proxyBeanMethods属性的值为true(默认就是true)的配置类,将会设置CGLIB代理子类替换原来的beanClass,后面再创建bean实例的时候,实际上就会创建一个代理子类对象。
这里代理子类对象,继承了目标代理类,因此这要求配置类对象不能是final修饰的,并且还实现了EnhancedConfiguration接口,而EnhancedConfiguration也实现了BeanFactoryAware接口,将会自动回调setBeanFactory方法。
被创建的CGLIB子类包括两个回调过滤器,一个是BeanMethodInterceptor拦截器,将会拦截配置类中的@Bean方法,另一个是BeanFactoryAwareMethodInterceptor拦截器,将会拦截setBeanFactory方法。所谓的“增强”,实际上就是对这两种方法的增强,而增强的具体实现就是通过这两个拦截器实现的!
对于被@Configuration注解及其派生注解标注的proxyBeanMethods属性的值为true(默认就是true)的配置类,内部的单例@Bean方法仅仅会被调用一次,用于创建对象,后续如果存在任何@Bean方法之间的调用,或者外部对象对@Bean方法的调用(这要求该对象交给Spring容器管理),都会是从容器中直接查找已创建的对象返回,不会再调用@Bean方法,能够保证是同一个实例,即都指向IoC容器内的单例,这就是full模式配置类的好处之一,但缺点就是由于生成了代理对象,造成性能有所缺失。
对于被@Configuration注解及其派生注解标注的proxyBeanMethods属性的值为true(默认就是true)的配置类,它内部的@Bean方法,如果是static方法,则对于访问修饰符不作任何限制,因为不需要进行代理拦截,因此也没有上面的好处;如果不是static方法,就要求@Bean方法不能被private或者final修饰,因为这需要进行代理拦截。
最后会添加一个ImportAwareBeanPostProcessor后处理器,用于调用setBeanFactory和setImportMetadata方法设置相关属性。
这两个方法还有很多细节没总结出来,具体内容应该仔细看前面的源码。另外,里面的很多重要方法的源码也没讲解,比如environment.resolveRequiredPlaceholders(location)是怎么解析占位符的、loadBeanDefinitions(resource)是怎么加载资源路径字符串的、registry.registerBeanDefinition具体是怎么注册bean定义的……等等这些方法由于都是IoC容器初始化的核心方法,因此在此前IoC容器初始化的整体流程中就重点讲过了,要想掌握这些知识点,那还需要花费大量时间,大家可以看我此前的文章,总计约20万字的IoC初始化整体流程:Spring 5.x 源码。
实际上,前面的文章加上该文章内容都学习完毕,那么Spring IoC的重要源码才算基本学习完毕,后面我们将介绍其他部分的核心源码,比如Spring AOP、事务机制等。