后置处理器ConfigurationClassPostProcessor如何解析注解
ConfigurationClassPostProcessor 简单概述
Spring 中类的解析是非常重要的,因为工程中有很多类,并且被一些注解修饰,比如:@Component、@Bean、@Import、@PropertySource、@ImportSource、@Scope 等。
你在类或者方法上标注这些注解,Spring 想要认识它,就需要通过 ConfigurationClassPostProcessor 类去解析扫描,以一定的形式加载到 Spring 中 (BeanDefinition),这样 Spring 才能够去使用它,并且去管理它。
ConfigurationClassPostProcessor 类是 BeanFactoryPostProcessor 接口的应用,在同类中它的执行优先级是最低的,它的作用就是将工程中的类 xxx.class 文件解析封装成 BeanDefinition,然后 Spring 就可以根据 BeanDefinition 模版生产 bean。
ConfigurationClassPostProcessor什么时候注册
xml方式的注册过程:
- AbstractApplicationContext#refresh
- AbstractApplicationContext#obtainFreshBeanFactory(refresh()步骤2)
- AbstractRefreshableApplicationContext#refreshBeanFactory
- AbstractXmlApplicationContext#loadBeanDefinitions
- AbstractBeanDefinitionReader#loadBeanDefinitions
- XmlBeanDefinitionReader#loadBeanDefinitions
- XmlBeanDefinitionReader#doLoadBeanDefinitions
- XmlBeanDefinitionReader#registerBeanDefinitions
- DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
- DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
- BeanDefinitionParserDelegate#parseCustomElement
- ComponentScanBeanDefinitionParser#parse
- ComponentScanBeanDefinitionParser#registerComponents
- AnnotationConfigUtils#registerAnnotationConfigProcessors
全注解方式的注册过程:
- AbstractApplicationContext#refresh
- AbstractApplicationContext#obtainFreshBeanFactory(refresh()步骤2)
- AbstractRefreshableApplicationContext#refreshBeanFactory
- AnnotationConfigWebApplicationContext#loadBeanDefinitions
- AnnotationConfigWebApplicationContext#getAnnotatedBeanDefinitionReader
- AnnotationConfigUtils#registerAnnotationConfigProcessors
ConfigurationClassPostProcessor什么时候实例化
- AbstractApplicationContext#refresh
- AbstractApplicationContext#invokeBeanFactoryPostProcessors(refresh()步骤5)
- PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
Spring 通过 getBeanNamesForType() 方法获取到所有实现 BeanDefinitionRegistryPostProcessor 接口的子类实现,其实目前只有一个那就是 ConfigurationClassPostProcessor,因为现在才刚开始阶段,类的解析都还没开始,所以 Spring 容器中基本上没什么东西。这里直接调用 getBean() 实例化 ConfigurationClassPostProcessor 类
然后下面就要开始调用 ConfigurationClassPostProcessor 这个类中的 postProcessBeanDefinitionRegistry() 方法去解析扫描其他类,就此类的解析流程就开始了!后面都是围绕着这个类进行解析。
ConfigurationClassPostProcessor如何解析
- ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
- ConfigurationClassPostProcessor#processConfigBeanDefinitions
主要流程如下:
1)先遍历所有被自动加载的 BeanDefinition 信息,一般都是会把标注了 @Configuration 注解的类优先进行注入,然后判断当前BeanDefinition属性 configurationClass 是否为空,若不为空,说明该类型已经被注入,否则就调用 Configuration 工具类检测类型是否标注了 @Configuration 注解,为其设置好相关的属性准备工作。
2)对配置类进行排序,根据 order 属性值,属性值越大的优先进行处理
3)判断是否有自定义的 beanName 生成器,若有进行实例化,否则不处理,一般无特殊的要求这里使用的就是 Spring 内部的生成器
4)若当前环境对象为空,就创建一个标准环境对象进行后续的值处理工作
5)实例化 ConfigurationClassParser 核心类,会调用其 parse 方法对配置类集合进行具体解析(parse->processConfigurationClass)
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 创建存放 BeanDefinitionHolder 对象集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 当前 registry 就是 DefaultListableBeanFactory,获取所有已经注册的 BeanDefinition beanName
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍历所有要处理 beanDefinition 名称,筛选对应 beanDefinition(被注解修饰的)
for (String beanName : candidateNames) {
// 获取指定名称 BeanDefinition 对象
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果 beanDefinition 中 configurationClass 属性不等于空,那么意味着已经处理过,输出日志信息
// 这里ConfigurationClassPostProcessor就无需再处理
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 判断当前 BeanDefinition 是否是一个配置类,并为 BeanDefinition 设置属性为 lite 或 full,此处设置属性值是为了后续进行调用
// 有@Configuration注解的为full
// 有@Component、@ComponentScan、@Import、@ImportResource注解,标准有@Bean注解的方法为lite
// 将有配置类注解的BeanDefinition,设置order属性值,用于后续的调用优先级
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 添加到对应的集合对象中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果没有发现任何配置类,则直接返回
if (configCandidates.isEmpty()) {
return;
}
// 如果适用,则按照先前确定的@Order的值排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
});
// 判断当前类型是否是 SingletonBeanRegistry 类型
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
// 类型的强制转换
sbr = (SingletonBeanRegistry) registry;
// 判断是否有自定义 beanName 生成器
if (!this.localBeanNameGeneratorSet) {
// 获取自定义 beanName 生成器
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
// 如果有自定义的命名生成策略
if (generator != null) {
// 设置组件扫描 beanName 生成策略
this.componentScanBeanNameGenerator = generator;
// 设置 import bean name 生成策略
this.importBeanNameGenerator = generator;
}
}
}
// 如果环境对象等于空,那么就重新创建新的环境对象
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 实例化 ConfigurationClassParser 类,并初始化相关的参数,完成配置类的解析工作
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 创建两个集合对象,
// 存放相关的BeanDefinitionHolder对象
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 存放扫描包下的所有bean
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 解析带有 @Controller、@Import、@ImportResource、@ComponentScan、@ComponentScans、@Bean 的 BeanDefinition
parser.parse(candidates);
// 将解析完的 Configuration 配置类进行校验
// 1、配置类不能是 final
// 2、@Bean 修饰的方法必须可以重写以支持CGLIB
parser.validate();
// 获取所有的bean,包括扫描的bean对象,@Import导入的bean对象
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 清除掉已经解析处理过的配置类
configClasses.removeAll(alreadyParsed);
// 判断读取器是否为空,如果为空的话,就创建完全填充好的 ConfigurationClass 实例的读取器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 核心方法,将完全填充好的ConfigurationClass实例转化为BeanDefinition注册入IOC容器
this.reader.loadBeanDefinitions(configClasses);
// 添加到已经处理的集合中
alreadyParsed.addAll(configClasses);
candidates.clear();
// 这里判断 registry.getBeanDefinitionCount() > candidateNames.length 目的是为了知道 reader.loadBeanDefinitions(configClasses) 这一步有没有向 BeanDefinitionMap 中添加新的BeanDefinition
// 实际上就是看配置类(例如 AppConfig 类会向 BeanDefinitionMap 中添加 bean)
// 如果有,registry.getBeanDefinitionCount() 就会大于 candidateNames.length
// 这样就需要再次遍历新加入的 BeanDefinition,并判断这些 bean 是否已经被解析过了,如果未解析,需要重新进行解析
// 这里 AppConfig 类向容器中添加 bean,实际上在 parser.parse() 这一步已经全部被解析了
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
// 如果有未解析的类,则将其添加到 candidates 中,这样 candidates 不为空,就会进入到下一次的 while 的循环中
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// 注册 ImportRegistry Bean 信息为了支持 ImportAware 配置类
if (sbr != null) {
if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// 清除相关的缓存,释放内存
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
- ConfigurationClassParser#parse
parse 方法存在于多个重载方法,但最终调用的都是 processConfigurationClass 方法
// 根据注解元数据、beanName 解析配置文件,有注解元数据
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
// 根据 Class、beanName 解析配置文件,有 Class 对象
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
// 根据 className、beanName 解析配置文件,读取元数据
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);
}
- ConfigurationClassParser#processConfigurationClass
1)调用 shouldSkip 方法,基于 @Conditional 标签判断该对象是否要跳过,如果不满足 @Conditional 中 value 中的条件,就跳过该 Bean,不会注入容器
2)判断该配置类是否已经被处理过:如果被处理过,则判断当前类和已配置过的类是否都是被 import 导入的,是则对两者的 importedBy 属性进行合并,否则就先进行移除然后重新加入配置类
3)前期的校验和准备工作做完以后,再调用 doProcessConfigurationClass 进行具体的注解解析工作
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断是否跳过解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 第一次进入的时候,configurationClass size 为 0,existingClass 肯定为 null,在此处理 configuration 重复 import
// 如果同一个配置类被处理两次,两次都属于被 import 则合并导入类返回,如果配置类不是被导入的,则移除旧的使用新的配置类
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
// 如果要处理的配置类 configClass 在已经分析处理的配置类记录中已存在,合并两者的importBy属性
existingClass.mergeImportedBy(configClass);
}
// 否则忽略新导入的配置类,因为存在 non-imported 类重写它
return;
}
else {
// 明确 bean 定义发现,可能取代了 import,允许移除老的已加载配置类
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 处理配置类,由于配置类可能存在父类(若父类的全类名是以java开头的,则除外),所有需要将 configClass变成 sourceClass 去解析,然后返回 sourceClass 父类
// 如果此时父类为空,则不会进行 while 循环去解析,如果父类不为空,则会循环的去解析父类
// SourceClass 意义:简单的包装类,目的是为了以统一的方式去处理带有注解的类,不管这些类是如何加载的
// 如果无法理解,可以把它当做一个黑盒,不会影响看 spring 源码主流程
SourceClass sourceClass = asSourceClass(configClass);
do {
// 解析各种注解
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// 将解析的配置类存储起来,这样回到 parse 方法时,能取到值
this.configurationClasses.put(configClass, configClass);
}
- ConfigurationClassParser#doProcessConfigurationClass
(1)processMemberClasses
递归处理内部类
// 递归处理内部类,因为内部类也是一个配置类,配置类上有 @configuration 注解,该注解继承 @Component,if 判断为 true,调用 processMemberClasses 方法,递归解析配置类中的内部类
processMemberClasses(configClass, sourceClass);
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 找到内部类,内部类中也可能是一个配置类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
// 如果不等于空的话
if (!memberClasses.isEmpty()) {
// 循环判断内部类是不是配置类 & 内部类不是当前配置类型
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 对配置类进行排序操作
OrderComparator.sort(candidates);
// 遍历符合规则的类
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
// 出现配置类循环导入,则直接报错
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 将配置类入栈
this.importStack.push(configClass);
try {
// 调用 processConfigurationClass 方法,因为内部类中还可能包含内部类,所以需要在做循环解析,实际工作中是不会有这中情况的
processConfigurationClass(candidate.asConfigClass(configClass));
}
finally {
// 解析完出栈
this.importStack.pop();
}
}
}
}
}
(2)processPropertySource
处理@PropertySource注解
// 如果配置类上加了 @PropertySource 注解,那么就解析加载 properties 文件,并将属性添加到 spring 上下文中
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
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;
}
// 获取value属性
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
// 处理属性值的占位符
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 将指定位置的资源转换成 resource 对象
Resource resource = this.resourceLoader.getResource(resolvedLocation);
// 添加 resource 对象为属性资源
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;
}
}
}
}
(3)处理 @ComponentScan、@ComponentScans 注解
1)通过注解工具类解析当前配置类中是否包含了 @ComponentScan、@ComponentScans 注解,存在就进行遍历挨个调用 ComponentScanAnnotationParser#parse 方法进行包的扫描工作。
扫描工作完成后,将满足条件的 BeanDefinitions 进行再次解析,对 BD 集合进行遍历,判别集合中的元素是否依然是配置类,是的话就继续往回走,调用 ConfigurationClassParser#parse 方法。
// 处理 @ComponentScan 或 @ComponentScans 注解,并将扫描包下的所有 bean 转换成填充后的 ConfigurationClass
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 比如 basePackages = com.harvey, 那么在这一步会扫描出这个包及子包下的 class,然后将其解析成 BeanDefinition(BeanDefinition 可以理解为等价于 BeanDefinitionHolder)
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 通过上一步扫描包 com.harvey,有可能扫描出来的 bean 中可能也添加了 ComponentScan 或 ComponentScans 注解.
// 所以这里需要循环遍历一次,进行递归(parse),继续解析,直到解析出的类上没有 ComponentScan、ComponentScans
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 判断是否是一个配置类,并设置 full 或 lite 属性
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
// 通过递归方法进行解析
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
2)ComponentScanAnnotationParser#parse 扫描包
创建实际进行扫描工作的类:ClassPathBeanDefinitionScanner,填充该类型所需用到的一些属性「scopedProxyMode、resourcePattern、includeFilters、excludeFilters、basePackages」。
调用 ClassPathBeanDefinitionScanner#parse 方法,扫描出 basePackages 属性包及子包下的 class,然后将其解析成 BeanDefinition 信息。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 创建对应的扫描类
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// 获取 @ComponentScan 参数,并进行参数的设置工作
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
// 获取 scopedProxy 属性
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
// 获取 resourcePattern 属性
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// 获取 includeFilters 属性
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 获取 excludeFilters 属性
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
// 获取 lazyInit 属性
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 获取 basePackages 属性
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
basePackages.addAll(Arrays.asList(tokenized));
}
// 获取 basePackageClasses 属性,对全限定类进行包名截取
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 开始执行扫描,最终的扫描器是 ClassPathBeanDefinitionScanner
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
3)ClassPathBeanDefinitionScanner#parse 进行实际的扫描工作
先调用 findCandidateComponents(backPackage) 将符合条件的 BeanDefinition(实际是 ScannedGenericBeanDefinition) 找出来。
处理 BeanDefinition 对象的属性信息,解析 @Scope 注解,设置 scopeName、proxyMode,通过 beanNameGenerator 生成 beanName。
设置自动装配属性,例如:该 bean 是否可以自动装配到其他 bean 中。
AnnotationConfigUtils#processCommonDefinitionAnnotations:处理定义在目标类上的通用注解,包括「@Lazy、@Primary、@DependsOn、@Role、@Description」填充对应的属性进去。
checkCandidate(beanName, candidate):检查 beanName 是否已经被注册过,如果被注册过考虑其是否与当前类兼容,不兼容就抛出异常,兼容则跳过当前 BeanDefinition 操作;如果未注册过,进行如下操作:
- 把当前遍历的 bean 定义信息和 beanName 封装成 BeanDefinitionHolder
- 调用 AnnotationConfigUtils.applyScopedProxyMode,根据 proxyMode 值来选择是否要创建代理,接口基于 JDK 动态代理,类基于 CGLIB 动态代理
- 主动 beanDefinition,放入到 BeanDefinitionMap、BeanDefinitionNames 中
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历basePackages
for (String basePackage : basePackages) {
// 扫描 basePackage,将符合要求 bean 定义全部找出来
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍历所有候选的 bean 定义
for (BeanDefinition candidate : candidates) {
// 解析 @Scope 注解,包括 scopeName 和 proxyMode
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 使用 beanName 生成器来生成 beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
// 处理 beanDefinition 对象,例如:此 bean 是否可以自动装配到其他 bean 中
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 处理定义在目标类上的通用注解,包括 @Lazy,@Primary,@DependsOn,@Role,@Description
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查 beanName 是否已经注册过,如果注册过,检查是否兼容
if (checkCandidate(beanName, candidate)) {
// 将当前遍历的 bean 定义、beanName 封装成 BeanDefinitionHolder
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 根据 proxyMode 值,选择是否创建作用域代理
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册 beanDefinition
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
4)processImports
处理 @Import 注解,导入额外的配置类,同时完成具体类的实例化工作,该类型处理也涉及到自动装配的工作。
processImports(configClass, sourceClass, getImports(sourceClass), true);
其中getImports递归获取被 @Import 注解标注的类,被它标注的类无须加 @Component、@Configuration 等配置注解,否则该 Bean 会被添加两次,但 Spring 会进行合并的工作
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
// 如果使用 @Import 注解修饰的类集合为空,那么直接返回
if (importCandidates.isEmpty()) {
return;
}
// 通过一个栈结构解决循环引入,栈中存在该配置类则抛出异常
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 添加到栈中,用于处理循环引入的问题
this.importStack.push(configClass);
try {
// 遍历每一个 @Import 注解的类
for (SourceClass candidate : importCandidates) {
// 检验配置类 Import 引入的类是否是 ImportSelector 子类
if (candidate.isAssignable(ImportSelector.class)) {
// 候选类是一个导入选择器->委托来确定是否进行导入
Class<?> candidateClass = candidate.loadClass();
// 通过反射生成一个 ImportSelect 对象
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 判断引用选择器是否是 DeferredImportSelector 接口的实例
// 如果是则应用选择器将会在所有的配置类都加载完毕后加载
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
// 将选择器添加到 deferredImportSelectorHandler 实例中,预留到所有的配置类加载完成后统一处理自动化配置类
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
// 获取引入的类,然后使用递归方式将这些类中同样添加了 @Import 注解引用的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归处理,被 Import 进来的类也有可能 @Import 注解
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 如果是实现了 ImportBeanDefinitionRegistrar 接口的 bd
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 候选类是ImportBeanDefinitionRegistrar -> 委托给当前注册器注册其他bean
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 放到当前 configClass 的 importBeanDefinitionRegistrars 中
// 在 ConfigurationClassPostProcessor 处理 configClass 时会随之一起处理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 候选类既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar-->将其作为 @Configuration 配置类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 如果 Import 类型是普通类,则将其当作带有 @Configuration 类一样处理
// 把 candidate 构造为 ConfigurationClass,标注为 importedBy,意味着它是通过被@Import 进来的
// 后面处理会用到这个判断将这个普通类注册进 DefaultListableBeanFactory
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
判断被 @Import 注解标注的类集合是否为空,不为空才进行后续的处理工作,循环遍历每一个配置类,判断它所匹配的类型
如果是 ImportSelector 接口的子类但非 DeferredImportSelector 接口的子类,就对其配置类进行递归处理,因为当前类可能还有使用 @Import 注解导入其他的配置类,递归调用的是 processImports 方法
如果是 ImportSelector & DeferredImportSelector 接口的子类,将其先暂时添加到 deferredImportSelectorHandler 集合中,待所有的配置类都加载完成以后:也就是当所有的类都调用 parse 方法结束后,再统一处理这些类型的配置类.
回忆一下,在 ConfigurationClassParser#parse 方法中会作这步操作 > this.deferredImportSelectorHandler.process();
如果是 ImportBeanDefinitionRegistrar 接口子类,会将其进行实例化后存入集合中,待所有配置类处理完后,调用其类下的 registerBeanDefinitions 设置具体的 BeanDefinition 类型,如 SpringBoot 中设置的就是专门由 Spring 内部使用的 BD
如果以上都不是的话,则将其当作带有 @Configuration 类一样处理,将 candidate 构造为 ConfigurationClass,标注为 importedBy,意味着它是通过被 @Import 进来的
5)处理 @ImportResource 注解
处理 @ImportResource 注解,导入 spring 配置文件,通过此方式引入的 xml 文件来通过 IOC 容器注入 Bean 实例对象
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
6)处理 @Bean 注解
检索配置类中加了 @Bean 注解的方法,将 @Bean 方法转化为 BeanMethod 对象,保存在集合中。
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
7)processInterfaces
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 找到配置类的所有接口,遍历接口
for (SourceClass ifc : sourceClass.getInterfaces()) {
// 找到含有 @Bean 注解的默认方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// 添加到集合中
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
// 递归处理,因为接口可能存在父接口
processInterfaces(configClass, ifc);
}
}
处理接口的默认方法实现,从 JDK8 开始,接口中的方法可以有自己的默认实现,若这个接口的方法加了 @Bean 注解,也需要被解析;然后检索配置类中加了 @Bean 注解的方法,将 @Bean 方法转化为 BeanMethod 对象,保存在集合中。
- ConfigurationClassParser#validate
public void validate() {
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
configClass.validate(this.problemReporter);
}
}
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
// 如果配置类是final类型,则抛出异常
if (getMetadata().isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
}
// 校验配置类中@Bean定义的方法
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
- ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
// 获取所有的 bean,包括扫描的 bean 对象,@Import 导入的 bean 对象
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 清除掉已经解析处理过的配置类
configClasses.removeAll(alreadyParsed);
// 判断读取器是否为空,如果为空的话,就创建完全填充好的 ConfigurationClass 实例的读取器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 核心方法,将完全填充好的ConfigurationClass实例转化为BeanDefinition注册入IOC容器
this.reader.loadBeanDefinitions(configClasses);
// 添加到已经处理的集合中
alreadyParsed.addAll(configClasses);
candidates.clear();
loadBeanDefinitions方法,实际上调用的是loadBeanDefinitionsForConfigurationClass
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 若 bean 通过 @Import(ImportSelector) 方式添加到容器中的,那么此时 configClass#isImported 返回的是 true
// configClass.importedBy 属性里面存储的是 ConfigurationClass 就是将 bean 导入的类
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 判断当前 bean 是否含有 @Bean 注解方法,如果有,需要把这些方法产生 bean 放入到 BeanDefinitionMap 当中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 将 @ImportResource 引入的资源注入 IOC 容器
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 如果 bean 上存在 @Import 注解,且 import 是一个实现了 ImportBeanDefinitionRegistrar 接口,则执行 ImportBeanDefinitionRegistrar#registerBeanDefinitions 方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}