Spring中的ConfigurationClassParser

ConfigurationClassParser类会被ConfigurationClassPostProcessor用来处理配置类,可以说该类完成了registry后置逻辑的大部分工作。本文分析该类的主要工作流程和实现原理。


该类ConfigurationClassParserConfigurationClassBeanDefinitionReader类共同组成了ConfigurationClassPostProcessor的registry的后置操作中的核心实现。

配置类判断

在介绍解析配置类之前,先看一下Spring对配置类的判断标准是怎样的。

checkConfigurationClassCandidate
candidateIndicators
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java
public static boolean checkConfigurationClassCandidate(
        BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

    String className = beanDef.getBeanClassName();
    // 存在factory-method的情况不算是配置类
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }

    // 获取目标类的元数据
    AnnotationMetadata metadata;
    // 可以直接获取元数据的情况
    if (beanDef instanceof AnnotatedBeanDefinition &&
            className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
        // Can reuse the pre-parsed metadata from the given BeanDefinition...
        // 直接获取元数据
        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.
        Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
        // 如果类属于下面几种,则不属于是配置类
        if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
                BeanPostProcessor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
                EventListenerFactory.class.isAssignableFrom(beanClass)) {
            return false;
        }
        // 根据Class来构建元数据
        metadata = AnnotationMetadata.introspect(beanClass);
    }
    else {
        try {
            // 获取元数据读取器
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
            // 读取元数据
            metadata = metadataReader.getAnnotationMetadata();
        }
        catch (IOException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Could not find class file for introspecting configuration annotations: " +
                        className, ex);
            }
            return false;
        }
    }

    // 如果存在@Configuration注解,那么获取注解内的proxyBeanMethods属性,并据此设置配置的模式(full还是lite)
    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
    // 如果存在@Configuration注解,且proxyBeanMethods属性为true,那么是FULL模式
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 如果没有@Configuration注解,但是类满足几点要求也算是配置类(具体是什么要求点进去看),不过是LITE模式
    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注解的value值
    Integer order = getOrder(metadata);
    if (order != null) {
        // 设置顺序
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }

    return true;
}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java
private static final Set<String> candidateIndicators = new HashSet<>(8);

// 如果类上面有下面四种注解,那么会认为该类是配置类
static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    // 不能是接口
    if (metadata.isInterface()) {
        return false;
    }

    // Any of the typical annotations found?
    /*
     * 只要被下面任意几个注解修饰,都算:
     * @Component
     * @ComponentScan
     * @Import
     * @ImportResource
     */
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }

    // Finally, let's look for @Bean methods...
    /*
     * 另外,就算只有@Bean修饰的方法,那么也算
     * 所以,并不一定必须在类上标注@Configuration注解
     * 但最好放在@Configuration类中,因为可以使用full模式的配置
     * 因为在full模式的情况下,方法会被代理,调用方法返回的是容器中的对象;
     * 而在lite模式的情况下,方法不会被代理,每次调用返回的都是新对象
     */
    return hasBeanMethods(metadata);
}

可以看到,只要类被@Component@ComponentScan@Import@ImportSource注解修饰,都算是配置类。其实@Configuration也被@Component修饰,只不过可以配置是用full还是lite模式,full模式会对类使用CGLIB增强。

ConfigurationClassPostProcessor中会调用ConfigurationClassParser类的parse方法,下面以该方法为切入口,来分析一下该类的工作原理。

parse方法主要流程

parse
processConfigurationClass
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    // 遍历配置类的bean定义
    for (BeanDefinitionHolder holder : configCandidates) {
        // 从holder对象中获取bean定义
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            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");
    /*
     * 在ConfigurationClassPostProcessor中传递进来的metadataReaderFactory是CachingMetadataReaderFactory类型的.
     * 这里的reader默认是SimpleMetadataReader类型的实例。
     */
    MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
    processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected final void parse(Class<?> clazz, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 检查一下条件注解
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    // 判断配置类是否已经处理过了
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        if (configClass.isImported()) { // 如果当前类已经导入过了
            if (existingClass.isImported()) { // 已存在的类也已经导入过了
                existingClass.mergeImportedBy(configClass); // 合并两个配置类,用当前类替换已存在的类的配置
            }
            // Otherwise ignore new imported config class; existing non-imported class overrides it.
            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.
    // 将配置类封装为sc对象,方便获取元数据
    SourceClass sourceClass = asSourceClass(configClass, filter);
    // 只要doProcessConfigurationClass方法不返回null,就一直递归。
    do {
        // 解析配置类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    // 添加配置类,这里的key-value都一样,为什么不用set?
    this.configurationClasses.put(configClass, configClass);
}

在下面3个重载的parse方法中,会创建ConfigurationClass对象,下面的处理过程中有些步骤会把解析结果保存到该对象的属性中,后续在ConfigurationClassBeanDefinitionReader中会处理。

该类的多个方法中大量使用了SourceClass这个类型,这个类是对配置类的元数据的封装。提供了很多getXXX方法来获取类的元数据,比如继承的父类、实现的接口、类上的注解、内部类等信息。知道该类可以做这些事情就可以了,暂时没必要直到是怎么获取的。

主要的逻辑在doProcessConfigurationClass方法中。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@Nullable
protected final SourceClass doProcessConfigurationClass(
        ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
        throws IOException {

    // 如果类上有@Component注解,其他衍生注解也算
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        // 递归处理内部类,也就是判断内部类是不是配置类
        processMemberClasses(configClass, sourceClass, filter);
    }

    // Process any @PropertySource annotations
    // 处理@PropertySources注解,遍历该注解中的@PropertySource注解数组
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            // 处理@PropertySource注解
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    // 处理@ComponentScan注解
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            // 判断当前配置类能否跳过,处理@Condition等条件注解
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // 遍历@ComponenetScan注解
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            // 扫描类路径下的bean,内部是会进行bean注册的,这里的返回值的作用是为了下面进行的递归解析。
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            // 遍历扫描结果
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                // 获取bean定义
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                // 解析到的bean属于配置类
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    // 进行递归解析
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    // 处理@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // Process any @ImportResource annotations
    // 处理@ImportResource注解
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        // 获取locations属性的值
        String[] resources = importResource.getStringArray("locations");
        // 获取reader属性的值
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        // 遍历资源路径位置
        for (String resource : resources) {
            // 解析占位符
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            // 添加导入源到配置类(ConfigurationClassPostProcessor)对象中
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    // 处理类中的@Bean方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 添加bean方法对象到配置类对象中
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    // 处理接口中的bean方法,也会将bean方法对象添加到配置类对象中
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    // 处理父类
    if (sourceClass.getMetadata().hasSuperClass()) {
        // 获取父类名称
        String superclass = sourceClass.getMetadata().getSuperClassName();
        // 存在父类,且父类不是JDK中的类,且没有处理过该父类(即knownSuperclasses中不存在)
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            // 放入集合中
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse

            // 返回父类,便于上层方法(processConfigurationClass方法)继续循环处理
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}

该方法较长,涉及的内容较多,但可以分为下面部分:

  • 处理内部类;
  • 处理@PropertySources注解;
  • 处理@ComponentScan注解;
  • 处理@Import注解;
  • 处理@ImportResource注解;
  • 处理@Bean方法;
  • 处理父类;

下面依次针对上面几个处理操作进行详细分析。

处理内部类

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
        Predicate<String> filter) throws IOException {

    // 获取类中的内部类信息
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
        List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
        // 遍历内部类
        for (SourceClass memberClass : memberClasses) {
            // 判断内部类是不是配置类
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                    // 筛选的是内部类,不能是当前类本身。这里对对上面调用的getMemberClasses方法不信任?
                    !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();
                }
            }
        }
    }
}

该类虽然较长,但是主要逻辑很清楚,就是递归调用上面介绍到的processConfigurationClass方法进行处理。

importStack记录当前调用栈中处理的配置类,避免出现循环导入的问题,否则那样会出现死循环。

处理@PropertySources注解

processPropertySource
createPropertySource
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {

    /*
     * 获取@PropertySource注解的各个属性的值
     */

    // 获取名称
    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");
    // 必须要求locations配置了值
    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));

    // 遍历每个location
    for (String location : locations) {
        try {
            // 解析占位符
            String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
            // 获取资源
            Resource resource = this.resourceLoader.getResource(resolvedLocation);
            // 添加数据源到environment中
            addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
        }
    }
}
java
spring-core/src/main/java/org/springframework/core/io/support/DefaultPropertySourceFactory.java
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
    return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}

该访问中主要获取@PropertySources注解中的属性,然后创建指定的属性文件的资源对象并创建属性源对象,最后添加到environment中。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private void addPropertySource(PropertySource<?> propertySource) {
    String name = propertySource.getName();
    // 获取environment中的已有属性源
    MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

    // 如果处理过了同名的属性源
    if (this.propertySourceNames.contains(name)) {
        // We've already added a version, we need to extend it
        // 获取已存在的属性源
        PropertySource<?> existing = propertySources.get(name);
        if (existing != null) {
            PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
                    ((ResourcePropertySource) propertySource).withResourceName() : propertySource);
            // 是不是复合属性源
            if (existing instanceof CompositePropertySource) {
                // 将新属性源添加到列表最前面
                ((CompositePropertySource) existing).addFirstPropertySource(newSource);
            }
            else {
                if (existing instanceof ResourcePropertySource) {
                    existing = ((ResourcePropertySource) existing).withResourceName();
                }
                // 创建复合属性源
                CompositePropertySource composite = new CompositePropertySource(name);
                // 将多个同名的属性源封装进一个复合属性源中
                composite.addPropertySource(newSource);
                composite.addPropertySource(existing);
                propertySources.replace(name, composite);
            }
            return;
        }
    }

    if (this.propertySourceNames.isEmpty()) {
        // 将属性源添加到environment中
        propertySources.addLast(propertySource);
    }
    else {
        // 这里明明是获取上一个处理的属性源,这里居然叫first?
        String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
        // 将新属性源添加在上一个添加的属性源之前,这也就是说后添加的优先级更高咯?
        propertySources.addBefore(firstProcessed, propertySource);
    }
    // 记录处理过的属性源
    this.propertySourceNames.add(name);
}

该方法首先检查了是否存在同名的数据源,如果是,则创建一个复合数据源来封装多个同名的数据源。然后将数据源添加到environment的数据源列表首部。

处理@ComponentScan注解

这部分内容很重要,可以说在开发业务系统中,大部分的bean都是通过这种方式注册的。比如@Controller@Service@Repository等注解都是通过这种方式进行扫描和注册的。这里是委托给ComponentScanAnnotationParser类来实现的,在ConfigurationClassParser的构造方法中创建的就是该类型的实例。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
        ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
        BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

    this.metadataReaderFactory = metadataReaderFactory;
    this.problemReporter = problemReporter;
    this.environment = environment;
    this.resourceLoader = resourceLoader;
    this.registry = registry;
    // 创建组件扫描解析器
    this.componentScanParser = new ComponentScanAnnotationParser(
            environment, resourceLoader, componentScanBeanNameGenerator, registry);
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}

下面就来看看ComponentScanAnnotationParser是怎么实现的。

java
spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
    // 创建类路径bean定义扫描器
    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));

    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));
    }

    // 设置类文件的模式,默认是ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN,表示子任意目录下的任意class文件
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
        // 根据@Filter注解(位于@ComponentScan注解文件中)来创建过滤器
        List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
                this.resourceLoader, this.registry);
        for (TypeFilter typeFilter : typeFilters) {
            // 设置包含过滤器
            scanner.addIncludeFilter(typeFilter);
        }
    }
    for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
        // 创建过滤器
        List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
            this.resourceLoader, this.registry);
        for (TypeFilter typeFilter : typeFilters) {
            // 设置排除过滤器
            scanner.addExcludeFilter(typeFilter);
        }
    }

    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        // 设置延迟初始化bean
        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));
    }
    // 如果没有设置basePackages或basePackageClasses,那么则扫描@Component所修饰的类及其子包下的类
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            // 排序@Component所修饰的类本身
            return declaringClass.equals(className);
        }
    });

    // 扫描bean
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

该方法看起来很长,其实就完成了三件事情:

  • 创建ClassPathBeanDefinitionScanner
  • 处理@ComponentScan注解中的属性;
  • 调用ClassPathBeanDefinitionScannerdoParse方法;

关于ClassPathBeanDefinitionScanner是怎么工作的,请参考Spring中的ClassPathBeanDefinitionScanner一文。

处理@Import注解

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
        boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }

    // 检查循环导入
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        // 报告循环导入错误
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            /*
             * 遍历import的每一个类,分为了三部分:
             * * ImportSelector
             * * ImportBeanDefinitionRegistrar
             * * other
             */
            for (SourceClass candidate : importCandidates) {
                // 处理ImportSelector
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    // 获取Class对象
                    Class<?> candidateClass = candidate.loadClass();
                    // 实例化目标类对象
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                            this.environment, this.resourceLoader, this.registry);
                    // 获取排除过滤器
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    if (selectorFilter != null) {
                        // 两个过滤器或上,任意一个满足即符合排除要求
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    // 特殊的导入选择器,需要在所有的配置类处理完成后才处理。
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        /*
                         * 执行选择导入,传入是导入的它的那个类的元数据信息,也就是@Import注解作用在的那个类。
                         * 这里也就是在向被导入方传入导入方的信息。
                         */
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        // 对需要导入的类创建sc对象
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        // 递归处理,所以这里看出来SelectImporter的作用就有点像是@Import注解的作用。
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                // 处理ImportBeanDefinitionRegistrar
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions

                    // 获取Class对象
                    Class<?> candidateClass = candidate.loadClass();
                    // 实例化目标类对象
                    ImportBeanDefinitionRegistrar registrar =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    /*
                     * 这里先add,之后在ConfigurationClassBeanDefinitionReader中会被处理。
                     */
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else { // 处理其他情况
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class

                    // 将候选者入栈
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    // 递归处理候选者,将其作为配置类对待,所以也说@Import导入类也具备配置类的效果。
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        }
        finally {
            this.importStack.pop();
        }
    }
}

该方法中针对导入的类的类型,分为下面三种情况:

  • 处理ImportSelector
  • 处理ImportBeanDefinitionRegistrar(简称”注册器“);
  • 处理普通类;

处理ImportSelector

这里是在调用ImportSelectorselectImports方法来获取需要导入哪些类,然后递归调用processImports方法来处理。

处理DeferredImportSelector

这部分比较重要,在Spring Boot中就是通过这种方式加载自动配置类的。因为Spring为延迟选择器引入了分组机制,所以整个处理过程稍微有一些复杂,注意理解。

这部分会涉及多个内部类,会比较绕,先来梳理一下:

  • DeferredImportSelectorHandler:选择器处理器,负责对外层提供处理接口,ConfugurationClassParser中的主要过程都是调用该类中的两个方法handleprocess
  • DeferredImportSelectorGroupingHandler:选择器分组处理器,负责对选择器进行分组,并调用每个分组的getImports方法来获取配置类。
  • DeferredImportSelectorGrouping:选择器分组包装类,封装了实际的DeferredImportSelector.Group这个组对象,内部保存了同一个组下的选择器列表。
  • DefaultDeferredImportSelectorGroup:默认的选择器分组类,如果选择器没有定义分组类型,即没有实现DeferredImportSelector接口的getImportGroup方法,则会使用默认的。
  • DeferredImportSelectorHolder:封装了选择器,以及导入该选择器的配置类对象。

下面来看这5个内部类是如何相互协调工作的。

首先通过选择器处理器(DeferredImportSelectorHandler)的handle方法来记录该延迟选择器。这里先将其封装到holder对象中(DeferredImportSelectorHolder),然后放到一个列表中,并没有立马调用它的selectImports方法,而这正是Deferred(推迟)的含义。那又是怎么调用到的呢?在上面介绍的parse方法中,等处理完当前配置类中的其他配置后,最后会调用选择器处理器的process方法来处理。

handle
process
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    // 将配置类封装进入holder对象
    DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
    // 这里默认不是null
    if (this.deferredImportSelectors == null) {
        // 创建处理器对象
        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
        handler.register(holder);
        handler.processGroupImports();
    }
    else {
        // 添加holder对象到列表中
        this.deferredImportSelectors.add(holder);
    }
}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            // 排序
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            // 注册到导入组中
            deferredImports.forEach(handler::register);
            // 执行组导入
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

在上面选择器处理器的process方法中,会先对选择器排序,然后通过选择器分组处理器(DeferredImportSelectorGroupingHandler)的register方法来分组。先获取选择器的分组对象,然后封装进入包装对象(DeferredImportSelectorGrouping),如果选择器没有定义分组对象,那么使用默认的分组对象(DefaultDeferredImportSelectorGroup)。因为Spring在这里使用的LinkedHashMap来保存分组包装对象,所以分组也是有序的,而是就是选择器的顺序。

register
processGroupImports
getCandidateFilter
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
// 可以保证顺序
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
// 这个方法其实是在对延迟导入选择器(DeferredImportSelector)进行分组
public void register(DeferredImportSelectorHolder deferredImport) {
    // 获取选择器的分组类型
    Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
    // 获取或创建grouping对象,这里的grouping对象封装了group类型的对象
    DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
            (group != null ? group : deferredImport),
            // 创建group类型的对象,然后封装到DeferredImportSelectorGrouping对象中
            key -> new DeferredImportSelectorGrouping(createGroup(group)));
    // 添加到组中
    grouping.add(deferredImport);
    this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getConfigurationClass());
}
private Group createGroup(@Nullable Class<? extends Group> type) {
    // 如果存在有效的组类型,则使用,默认的组类型是DefaultDeferredImportSelectorGroup
    Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
    // 创建分组对象
    return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
            ConfigurationClassParser.this.environment,
            ConfigurationClassParser.this.resourceLoader,
            ConfigurationClassParser.this.registry);
}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
// 可以保证顺序
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
		public void processGroupImports() {
			// 遍历每个导入组
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				// 遍历导入组中的导入
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
						// 调用processImports执行导入,在整个大流程范围内算是递归调用
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
				});
			}
		}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public Predicate<String> getCandidateFilter() {
    Predicate<String> mergedFilter = DEFAULT_EXCLUSION_FILTER;
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        // 获取选择器的过滤器
        Predicate<String> selectorFilter = deferredImport.getImportSelector().getExclusionFilter();
        if (selectorFilter != null) {
            // 和其他选择器的过滤器或上
            mergedFilter = mergedFilter.or(selectorFilter);
        }
    }
    return mergedFilter;
}

processGroupImports方法中,先调用分组包装对象的getCandidateFilter来处理多个选择器的排除过滤器,然后会调用分组包装对象的getImports方法来获取配置类,最后调用上面介绍过的processImports方法来递归处理获取到的每个配置类。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

这里会调用实际的Group对象的process方法来准备配置类,然后调用selectImports方法来获取配置类。比如在Spring Boot中的AutoConfigurationImportSelector就是用的自动配置组AutoConfigurationGroup

如果有的选择器没有定义分组类型,那么会使用默认的分组。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private static class DefaultDeferredImportSelectorGroup implements Group {

    private final List<Entry> imports = new ArrayList<>();

    @Override
    public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
        // 直接调用其selectImports方法
        for (String importClassName : selector.selectImports(metadata)) {
            this.imports.add(new Entry(metadata, importClassName));
        }
    }

    @Override
    public Iterable<Entry> selectImports() {
        return this.imports;
    }
}

处理ImportBeanDefinitionRegistrar

对与该类型的类,没有做太多的操作,仅仅是添加到配置类的集合属性中。后续ConfigurationClassPostProcessor会调用ConfigurationClassBeanDefinitionReader来处理。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
        new LinkedHashMap<>();
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
    this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

处理普通类

这种情况的处理就非常简单了,直接调用processConfigurationClass方法来递归处理。

处理@ImportResource注解

@ImportResource注解可以导入其他spring配置文件,比如在传统的SSM应用中使用很多的XML配置文件,不过目前在Spring Boot中就很少使用了。但还是简单看一下这里是怎么处理的。 和上面的ImportBeanDefinitionRegistrar类似,这里也是把注解中的路径和文件读取器信息添加到配置类的对象中,待后续ConfigurationClassBeanDefinitionReader来处理。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
        new LinkedHashMap<>();
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
    this.importedResources.put(importedResource, readerClass);
}

处理bean方法

处理当前类本身中的bean方法

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    // 获取类的元数据
    AnnotationMetadata original = sourceClass.getMetadata();
    // 获取所有被@Bean注解修饰的方法
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    // 一般考虑这里是SimpleAnnotationMetadata,所以这里不满足条件
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
    }
    return beanMethods;
}

该方法中,获取了类中所有被@Bean注解修饰的方法。 获取到bean方法中,还是添加到配置类的属性中,待后续ConfigurationClassBeanDefinitionReader来处理。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
void addBeanMethod(BeanMethod method) {
    this.beanMethods.add(method);
}

处理实现的接口中的bean方法

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    // 获取当前类实现的所有接口
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        // 获取接口中的被@Bean注解修饰的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
        // 遍历bean方法
        for (MethodMetadata methodMetadata : beanMethods) {
            // 如果不是抽象方法
            if (!methodMetadata.isAbstract()) {
                // A default method or other concrete method on a Java 8+ interface...
                // 添加bean方法对象
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
        // 递归处理父接口
        processInterfaces(configClass, ifc);
    }
}

这里会处理当前接口及其父接口中的bean方法,然后添加到配置类的属性中。

处理父类

这里通过knownSuperclasses属性来避免多次处理到同一个父类。

doProcessConfigurationClass方法中,如果当前配置类的父类还没被处理过,那么会返回父类的SourceClass对象,在上层processConfigurationClass的do...while循环中会递归处理。processConfigurationClass方法的代码在上面就已经给出了,这里再次贴出一下,重点关注方法中的循环部分,就是处理父类的实现。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 检查一下条件注解
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    // 判断配置类是否已经处理过了
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        if (configClass.isImported()) { // 如果当前类已经导入过了
            if (existingClass.isImported()) { // 已存在的类也已经导入过了
                existingClass.mergeImportedBy(configClass); // 合并两个配置类,用当前类替换已存在的类的配置
            }
            // Otherwise ignore new imported config class; existing non-imported class overrides it.
            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.
    // 将配置类封装为sc对象,方便获取元数据
    SourceClass sourceClass = asSourceClass(configClass, filter);
    // 只要doProcessConfigurationClass方法不返回null,就一直递归。
    do {
        // 解析配置类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    // 添加配置类,这里的key-value都一样,为什么不用set?
    this.configurationClasses.put(configClass, configClass);
}

总结

本文介绍了ConfigurationClassPostProcessor中registry的后置操作的核心过程。Spring Boot中大量使用的注解配置大部分最终都会在本文涉及的过程中来实现,这样Spring Boot中的一些配置过程就和容器刷新这条主线关联上了。