12-配置类解析
12-配置类解析
前言
上一篇文章分析Spring启动流程中有一个步骤是执行BeanFactoryPostProcessor,在refresh-->invokeBeanFactoryPostProcessors(beanFactory),该方法中会先执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,然后执行postProcessBeanFactory方法,其中ConfigurationClassPostProcessor 是Spring内置的一个BeanDefinitionRegistryPostProcessor的实现类**,** 在创建AnnotatedBeanDefinitionReader
的过程中它对应的BeanDefinition就被注册到容器中了。接下来我们就来分析**ConfigurationClassPostProcessor
**这个类的源码
ConfigurationClassPostProcessor源码分析
它实现了BeanDefinitionRegistryPostProcessor
,所以首先执行它的**postProcessBeanDefinitionRegistry
**方法,其源码如下
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 这里生成一个注册IDregistryId,为了防止同一个注册器多次调用,SET集合本身具有去重功能,所以用SET存放
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
// 表明这个工厂已经经过了后置处理器了
this.registriesPostProcessed.add(registryId);
/**
* 这个方法是配置类处理的核心所在,它做的时期:
* 1.处理@Component注解;
* 2.处理@ComponentScan注解;
* 3.处理@PropertySource
* 4.处理@Import注解;
* 5.处理@ImportResouce注解
*
* 简单来说就是根据我们的配置类找到所有符合条件的class对象,然后生成BeanDefinition,放入beanDefinitionMap中
*/
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions
processConfigBeanDefinitions
方法的代码很长,我们拆分一段段分析,先看第一段
第一段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// ========第一段代码========
// 声明一个list,用来存放我们的配置类(什么配置类??),比如我们的配置类是AppConfig,
// 这个类我们是通过spring提供的方法register注册到DefaultListableBeanFactory的bdmap中的
// 所以这里的configCandidates主要为了先存放我们的配置类,因为配置类可以有多个,所以这里是一个集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 这里是拿到容器中所有的BeanDefinition的名字,如果你只设置了一个配置类,你没有手动添加一些bean工厂的后置处理器的话,那么这里
// 拿到的就只有6个,有5个bd是spring的内置的bd,有一个bd是你的配置类,而这里我们主要就是拿到这个配置bd,然后进行处理
// ???
String[] candidateNames = registry.getBeanDefinitionNames();
// 循环处理这个bdmmap集合
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//这里判断我们的BeanDefinition中是否有一个configurationClass属性,configurationClass是配置类信息封装对象
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 检查是否是配置类,在这里会将对应的bd标记为FullConfigurationClass或者LiteConfigurationClass
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 是配置类的话,将这个bd添加到configCandidates中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// 没有配置类,直接返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
/**
* 对我们扫描到的配置bd进行排序,看谁先执行,就是说我们如果希望你设置的比如3个配置类,那个先执行,那个后执行, 也就是说
* 你可以控制它的执行顺序,如果这样的话,你可以实现了一个Order接口,实现了Order接口,根据Order的大小进行排序,最后根据Order最小
* 的最先执行,其他的依次执行,如果你都没有加Order,那么这个Order的值默认是Integer.Max_VAlue
*/
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// ...
上面这段代码有这么几个问题:
1.当前容器中有哪些BeanDefinition
在上篇文章分析启动流程中,Spring在创建AnnotatedBeanDefinitionReader对象的时候Spring已经往容器中注册了5个内置的BeanDefinition,再加上我们自己注册的1个配置类,那么此时容器中应该存在6个BeanDefinition,我们可以打个断点验证
2.检查是否是配置类:checkConfigurationClassCandidate
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
// 下面这一段都是为了获取一个AnnotationMetadata
// AnnotationMetadata包含了对应class上的注解元信息以及class元信息
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
// 已经解析过了,比如注册的配置类就属于这种,直接从bd中获取
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
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.
// 拿到字节码重新解析获取到一个AnnotationMetadata
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
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 {
// class属性都没有,就根据className利用ASM字节码技术获取到这个AnnotationMetadata
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;
}
}
/**
*这里面的逻辑就简单描述下:
* 1.你的配置类是否加了@Configuration注解,如果加了,并且@Configuration中的proxyBeanMethods是true的话,那么加一个full,表示
* 全注解,需要生成配置类的代理对象(cglib),如果你加了@Configuration,但是你的代理方法proxyBeanMethods是false的话,那么也不是一个全注解
* 也不加full属性,所以配置类的代理对象是根据是是否加了@Configuraiton,如果加了,是否重新定义了proxyBeanMethods这个属性.
*
* 2.如果没有加@Configuration注解,判断你是否有@Component、@ComponScan、@Import、@ImportResouce注解
* 如果加了这些注解的一个或者多个,都认为是一个配置类,但是不是全注解,bd中设置一个参数lite
*/
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果被@Configuration注解标注了,说明是一个FullConfigurationCandidate
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 如果被这些注解标注了,@Component,@ComponentScan,@Import,@ImportResource
// 或者方法上有@Bean注解,那么就是一个LiteConfigurationCandidate
// 也就是说你想把这个类当配置类使用,但是没有添加@Configuration注解
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
// 解析@Order注解,用于排序
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
第二段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 第一段
// .....
// ========第二段代码========
// Detect any custom bean name generation strategy supplied through the enclosing application context
// 我们的注册器BeanDefinitionRegistry肯定是SingletonBeanRegistry,看工厂类DefulatListableBeanFactory就知道
// 这里就是判断你应用是否实现了beanName的生成器,如果没有就使用默认的生成器
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
// beanName的生成策略
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
// 因为后置的扫描要处理@ProperResouce注解,所以如果这里之前没有设置environment,那么这里重新创建
// 其实在spring启动准备的时候就已经加了这个环境的bean了,所以这里肯定是不能为空,只是spring设计的严谨一点
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 核心目的就是创建这个ConfigurationClassParser对象,配置类解析器,用来解析我们的配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 第三段
第三段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 第一段,第二段
// .....
// 这里重新申明了一个SET结合来存放我们上面得到的配置类,为什么要用一次set来存放?(set去重,set底层实现就是map的key,所以是可以去重的)
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 存放容器中已经被处理过的配置类集合
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
/**
* 下面是开启了一个do while 循环,每次拿到的配置类集合进行处理,因为每次处理完成过后还可能出现新的配置类,所以这里每次
* 获取配置类列表,然后处理,处理完成过后又重新获取新的配置类列表,直到将所有的配置类处理完成
*
*/
do {
// 在第二段代码中创建了一个ConfigurationClassParser,这里就是使用这个parser来解析配置类
// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass
// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass
parser.parse(candidates);
// 校验在解析过程是中是否发生错误,同时会校验@Configuration注解的类中的@Bean方法能否被复写(被final修饰或者访问权限为private都不能被复写),如果不能被复写会抛出异常,因为cglib代理要通过复写父类的方法来完成代理
parser.validate();
/**
* 将扫描到的配置类都放入了parser.getConfigurationClasses(),所以这里获取扫描得到的配置类列表;
* 这里说明一下,在spring的扫描架构中,只要是扫描到的@Component它都认为一个配置类
* 1.@Compoent为一个配置类,表示被扫描到的;
* 2.@Import为一个配置类,其中包含了属性ImportBy,表示被谁导入的;(扩展点)
* 3.beanMethods表示配置类中的@Bean方法列表;
* 4.importedResources属性表示配置类要导入的资源列表;
* 5.importBeanDefinitionRegistrars属性表示被@Import导入的Bean定义注册器(扩展点)
*/
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 移除已经解析过的配置类,防止重复加载了配置类中的bd
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 将通过解析@Bean,@Import等注解得到相关信息解析成bd被注入到容器中
this.reader.loadBeanDefinitions(configClasses);
// 将已经处理过的配置类放入已处理的列表中
alreadyParsed.addAll(configClasses);
// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClass
// 将当前已经处理过的配置类集合清除(它是do while循环退出的依据)
candidates.clear();
/**
* 如果说从容器中拿到的的BeanDefinition的数量是大于一开始获取的BeanDefinition数量candidateNames.length
* 那么就证明在上面的解析配置类的过程中出现了新的配置类(肯定会出现的,配置类只要满足@Component就会成为一个配置类)
* 所以它又还会去处理,但是在parse方法里面已经做了去重,@Component中如果有@Bean这些也会进行处理
* 所以这里再次去处理也是没有问题的;下面代码的逻辑:
* 重新获取系统中所有的BeanDefinition,然后筛选掉上面已经处理过的BeanDefinition,然后去检查是否
* 是配置类的候选者,也就是判断是否是一个配置类,如果是一个配置类,那么添加到配置类处理集合candidates,
* 再次去循环,直到系统中的所有配置类都已经被处理过
*/
// 如果大于,说明容器中新增了一些bd,那么需要重新判断新增的bd是否是配置类,如果是配置类,需要再次解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
// 从容器中重新拿出来的所有的Beand的名字集合
String[] newCandidateNames = registry.getBeanDefinitionNames();
// 上一次获取的BeanDefinition名字集合
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
// 从之前到现在已经解析过的配置类名字集合
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
// 循环新获取的配置类集合名字
for (String candidateName : newCandidateNames) {
// 去掉已经处理过的
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
// 如果符合配置类的候选者判断,然后加入到处理集合列表中,开始新一轮的循环
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// 注册ImportRegistry到容器中
// 当通过@Import注解导入一个全配置类A(被@Configuration注解修饰的类),A可以实现ImportAware接口
// 通过这个Aware可以感知到是哪个类导入的A
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
1.从BeanFactory中(属性beanDefinitionNames)中拿到所有的beanName的名字;
2.循环这个beanNames,根据beanName去beanDefinitionMap获取BeanDefinition,然后判断这个BeanDefinition是否是一个配置类,配置类的检查条件是是实现了@Configutation、@Component、@ComponScan、@Import、@ImportResource、@Bean的条件之一都满足,唯一有一个区别就是如果是加了@Configuration注解的配置类,那么会设置一个属性full表示是全注解的配置类,否则就是一个lite属性,表示部分注解的配置类,如果是全注解的配置类,那么这个配置类最后也是在单例池中生成一个对象,这个对也是一个代理对象,否则就是一个简单Bean对象;满足条件的BeanDefinition就会添加一个配置类的处理集合中candidates;
3.如果筛选出来的candidates集合不为空,那么就开始do while循环,然后循环中调用配置类解析方法parse去解析,解析主要处理了@Component、@ComponScan、@Import、@ImportResource、@Bean这个几个注解:
a.判断是否加了@Componet注解,如果加了,处理@Component所在类的内部类,因为内部类可能有其他注解,然后这里是一个递归处理;
b.判断是否有@ProperResource注解,如果有,处理@ProperResource,将配置的资源文件解析出来放入到Environment中;
c.处理@ComponScan注解,执行真正的扫描工作,将符合条件的创建ConfigurationClass;
d.处理@Import注解,@Import注解有三种类型,普通类的导入,ImportSelector,ImportBeanDefinitionRegistrar;
e.处理@ImportResource注解,@ImportResouce注解一般导入的是spring的配置文件,配置的是;
f.处理每个配置类的@Bean方法。
配置类解析完成过后,然后do while循环中又重新重容器中拿到BeanDefinition中的数量,然后和之前获取的数量进行比较,最后找出新产生的,最新产生的如果也是满足配置类的检查条件,又加入到candidates中进行循环调用parse方法,直到将容器中的所有配置类都解析完成,就是将系统中所有符合条件的每个配置类都解析成BeanDefinition,直到全部处理完成过后,那么就退出循环, 配置类的解析工作就完成了。
ConfigurationClassParser源码解析
配置类解析器,这个是配置类的解析器,是spring内置的解析器类,我们看下上面的代码调用配置类的解析器中的parse方法进行解析的,所以我们看下这个类的parse方法如下:
parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 遍历所有的配置类,一个个完成解析
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 三个判断最终都会进入到同一个方法---->processConfigurationClass方法
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 对ImportSelector进行延迟处理
this.deferredImportSelectorHandler.process();
}
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);
}
processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 条件注解,就是看有没有类上是否有@Conditional注解,如果有,则进行条件匹配
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判断解析器是否已经解析过这个配置类了
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
// 不为null,说明已经解析过了
if (existingClass != null) {
// 如果这个要被解析的配置类是被@Import注解导入的
if (configClass.isImported()) {
// 并且解析过的配置类也是被导入的
// OrderService导入了AccountService,UserService也导入了AccountService,就会符合这个条件
if (existingClass.isImported()) {
// 那么这个配置类的导入类集合中新增当前的配置类的导入类
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
// 如果已经解析过的配置类不是被导入的,那么直接忽略新增的这个被导入的配置类。也就是说如果一个配置类同时被@Import导入以及正常的
// 添加到容器中,那么正常添加到容器中的配置类会覆盖被导入的类
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
/**
* 这里进行循环处理,条件是每次返回的sourceClass不为空,,在doProcessConfigurationClass方法中
* 每次处理完成过后,会获取这个类的父类,因为可能父类中存在注册信息,所以这里的循环其实就是以当前类为准备,一直进行扫描
* 处理,直到把所有的父类都处理完成,才结束,如果说处理到没有父类的时候,那么doProcessConfigurationClass方法
* 会返回一个空,就会跳出循环
*/
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 每个配置类扫描处理完成过后都加入到configurationClasses
// ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以
this.configurationClasses.put(configClass, configClass);
}
doProcessConfigurationClass方法
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
/**
* 判断我们的注解是否实现了@Component,这个判断是什么意思呢?就是说如果你的配置类比如Appconfig中加了
* @Component注解,那么要先看处理这个配置类的内部类,万一内部类定义了@Bean或者@CompoenScan注解
* 所以这里先处理内部类,如果有内部类,先把内部类处理完成,如果内部类也有配置类,那么也会当成是一个配置类
* 的候选者,内部类的配置类的候选者判断条件和配置类的候选条件判断一致
*/
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 处理内部类
// 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
// 处理@PropertySources跟@PropertySource注解,将对应的属性资源添加容器中(实际上添加到environment中)
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 处理@ComponentScan,@ComponentScans注解,真正进行扫描的地方就是这里
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 核心代码,在这里完成的扫描
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
// 检查扫描出来的bd是否是配置类,如果是配置类递归进行解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 判断扫描出来的bd是否是一个配置类,如果是的话继续递归处理
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 处理@Import注解
// getImports(sourceClass)会拿到@Import导入的类
// 如果导入的是普通类,那么会直接把它当做配置类来解析
// 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()
// 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()
// 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
// 处理@ImportResource注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
// 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 处理接口中的default方法
/**
* 其实下面的processInterfaces接口处理,主要是处理java8中接口有默认实现的逻辑处理
* 默认接口也可以使用@Bean来定义一个Bean
*/
processInterfaces(configClass, sourceClass);
// Process superclass, if any
/**
* 程序执行到这里过后,当前的配置类已经处理完成了,这里获取它的父类,看是否有父类,如果有父类,返回父类,在do while循环中继续处理
* 如果没有父类,当前配置类就扫描结束了
*/
// 返回父类,进行递归处理
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
可以看到,在doProcessConfigurationClass真正完成了对配置类的解析,一共做了下面几件事
- 解析配置类中的内部类,看内部类中是否有配置类,如果有进行递归处理
- 处理配置类上的@PropertySources跟@PropertySource注解
- 处理@ComponentScan,@ComponentScans注解
- 处理@Import注解
- 处理@ImportResource注解
- 处理@Bean注解
- 处理接口中的default方法
- 返回父类,让外部的循环继续处理当前配置类的父类
我们逐一进行分析
@Component内部类处理
/**
* Register member (nested) classes that happen to be configuration classes themselves.
* 这里是处理内部类的,获取配置类中的所有内部类,然后判断是否是一个配置类的候选者,如果是配置类的候选者
* 那么循环调用处理配置类的处理过程,将每个内部类进行处理,处理流程和主配置类的处理流程一样,也会调用到
* processConfigurationClass这个方法里面,就是一个递归调用,如果内部类的条件是满足的,那么就会进行递归调用,直到内部类处理完成
*/
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
//循环获取内部类,然后判断是否是一个配置类的候选者,如果是,加入candidates
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
//进行循环递归调用
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
@PropertySources配置文件加载
/**
* Process the given <code>@PropertySource</code> annotation metadata.
* @param propertySource metadata for the <code>@PropertySource</code> annotation found
* @throws IOException if loading a property source failed
* 这个方法是处理@PropertySource的逻辑,就是读取出来@PropertySource中的属性,然后得到资源路径列表,然后选好处理
* 将资源文件中的key value读取出来加入到Environment中
*/
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
//这里是处理$占位符的,得到一个真实的路径
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
//然后加入到环境变量中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException 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;
}
}
}
}
@ComponScan处理
org.springframework.context.annotation.ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 第一步就创建了一个ClassPathBeanDefinitionScanner对象
// 在这里我们就知道了,Spring在进行扫描时没有使用在最开始的时候创建的那个对象进行扫描
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// 解析成bd时采用的beanName的生成规则
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
// 配置这个扫描规则下的ScopedProxyMode的默认值
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));
}
// 配置扫描器的匹配规则
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// 配置扫描器需要扫描的组件
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 配置扫描器不需要扫描的组件
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
// 配置默认是否进行懒加载
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 配置扫描器扫描的包名
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);
Collections.addAll(basePackages, tokenized);
}
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);
}
});
// 在完成对扫描器的配置后,直接调用其doScan方法进行扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
第一步就创建了一个ClassPathBeanDefinitionScanner
,紧接着通过解析注解,对这个扫描器进行了各种配置,然后调用doScan方法完成了扫描,扫描的源码在bean的生命周期的文章中分析过了。
处理@Import注解
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 没有要导入的类,直接返回
if (importCandidates.isEmpty()) {
return;
}
// checkForCircularImports:Spring中写死的为true,需要检查循环导入
// isChainedImportOnStack方法:检查导入栈中是否存在了这个configClass,如果存在了说明
// 出现了A import B,B import A的情况,直接抛出异常
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 没有出现循环导入,先将当前的这个配置类加入到导入栈中
this.importStack.push(configClass);
try {
// 遍历所有要导入的类
for (SourceClass candidate : importCandidates) {
// 如果要导入的类实现了ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 反射创建这个ImportSelector
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);
}
// 如果import的是DeferredImportSelector,表示推迟导入
// 如果是一个DeferredImportSelector,添加到deferredImportSelectors集合中去
// 在所有的配置类完成解析后再去处理deferredImportSelectors集合中的ImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 不是一个DeferredImportSelector,那么通过这个ImportSelector获取到要导入的类名
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归处理要导入的类
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
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 最后将其添加到configClass的importBeanDefinitionRegistrars集合中
// 之后会统一调用其ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,将对应的bd注册到容器中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 既不是一个ImportSelector也不是一个ImportBeanDefinitionRegistrar,直接导入一个普通类
// 并将这个类作为配置类进行递归处理
// 注意,在asConfigClass方法中,不仅会将candidate生成一个ConfigurationClass,还会记录一下candidate是被哪个类导入的importedBy
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
// 在循环前我们将其加入了导入栈中,循环完成后将其弹出,主要是为了处理循环导入
this.importStack.pop();
}
}
这里是@Import的三种类型,分别是Import的普通类,ImportSelector,ImportBeanDefinitionRegistrar
1.如果导入的是普通类,那么会递归去调用processConfigurationClass,将普通类注册成一个ConfigClass,它有一个属性为ImportBy,这个Importby就表示是谁将它导入的
2.如果是ImportSelector,那么会调用它的selectImports返回一个列表,这个列表也是要被导入的类列表,类可能是普通类,可能是ImportSelector,也有可能是ImportBeanDefinitionRegistrar,
如果是普通类,进入普通类的逻辑,如果还是ImportSelector,又递归,如果是ImportBeanDefinitionRegistrar,添加到ConfigClass中属性importBeanDefinitionRegistrars集合中
3.如果是ImportBeanDefinitionRegistrar,就直接添加到ConfigClass中属性importBeanDefinitionRegistrars集合中;
处理@ImportResource注解
/**
* 下面的是处理@ImportResouce("classpath:spring.xml"),类似于这种,获取locations列表,然后循环去处理
* 其中locations可以是通过占位符来实现,然后将占位符处理过后添加到ConfigClass中的importedResources的资源集合中
*/
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) {
//占位符处理,占位符可以是@ImportResouce("${xxxx}")
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
//将占位符处理过后,添加到集合中
configClass.addImportedResource(resolvedResource, readerClass);
}
}
处理@Bean注解
将配置类中所有的被@Bean标注的方法添加到configClass的BeanMethod集合中
处理接口中的default方法
代码也很简单,Java8中接口能定义default方法,这里就是处理接口中的default方法,看其是否有@Bean标注的方法
到此为止,我们分析完了整个解析的过程。可以发现Spring将所有解析到的配置信息都存储在了**ConfigurationClass
**类中,但是到目前为止这些存储的信息都没有进行使用。那么Spring是在哪里使用的这些信息呢?回到我们的第三段代码,其中有一行代码如图所示:
loadBeanDefinitions方法
// configurationModel:被解析完成了配置类集合,其中保存了@Bean注解解析信息,@Import注解解析信息等等
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 调用这个方法完成的加载
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 判断是否需要跳过,例如A导入了B,A不满足加载的条件需要被跳过,那么B也应该被跳过
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;
}
if (configClass.isImported()) {
// 将被导入的类生成BeanDefinition并注册到Spring容器中
// @Component的内部类,@Import所导入的类都是被导入的类
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
// 解析@Bean标注的Method得到对应的BeanDefinition并注册到容器中
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 解析导入的配置文件,并将从中得到的bd注册到容器中
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 执行configClass中的所有ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
参考文章: