后置处理器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());
}

 

posted @ 2023-12-08 15:36  残城碎梦  阅读(99)  评论(0编辑  收藏  举报