spring容器加载分析 三Configuration类解析

Spring中对Configuration类的解析是通过ConfigurationClassPostProcessor进行的,这个类是BeanFactoryPostProcessor的实现,在容器刷新方法中invokeBeanFactoryPostProcessors(beanFactory)这个方法调用所有的BeanFactoryPostProcessor,同时也就启动了Configuration类解析的。
解析的总体过程:

1、从Bean工厂找出所有Configuratio类加入configCandidates列表中,所谓Configuratio类就是被@Configuration注解的类或者包含@Bean、@Component、@ComponentScan、@Import、@ImportResource注解的类。
2、根据@Order对configCandidates列表进行排序
3、遍历configCandidates,使用委托类ConfigurationClassParser解析配置项,包含@PropertySources注解解析、@ComponentScan注解解析、@Import注解解析、@Bean注解解析。
4、遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的BeanDefinition

具体配置项解析过程在ConfigurationClassParser类中实现:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {
    // Recursively process any member (nested) classes first
    processMemberClasses(configClass, sourceClass);
    // 1、处理@PropertySources注解,解析属性文件
    // 将解析出来的属性资源添加到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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }
    // 2、处理@ComponentScan注解,通过ComponentScanAnnotationParser解析@ComponentScan注解
    Set 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 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) {
                // 检查是否是ConfigurationClass,如果是走递归
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
                    parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }
    //3、处理@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    // Process any @ImportResource annotations
    // 处理@ImportResource注解:获取@ImportResource注解的locations属性,得到资源文件的地址信息。
    // 然后遍历这些资源文件并把它们添加到配置类的importedResources属性中
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        String[] resources = importResource.getStringArray("locations");
        Class readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }
    // 4、处理@Bean注解:获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
    Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // 处理接口和父类
    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);
    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (!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;
}

step 1 处理@PropertySources注解
解析属性文件,将解析出来的属性资源添加到Environment中
step 2 处理@ComponentScan注解
通过ComponentScanAnnotationParser解析@ComponentScan注解,解析方法为parse:
2.1)、实例化元数据(注解)扫描器ClassPathBeanDefinitionScanner
2.2)、分析出Bean名称生成器BeanNameGenerator
2.3)、分析出代理模型ScopedProxyMode
2.4)、分析出resourcePattern,默认值"*/.class"
2.5)、分析出扫描包含的目录includeFilters、排除的目录excludeFilters,生成过滤规则
2.6)、分析出加载类型,延迟或非延迟
2.7)、将上述属性设置到ClassPathBeanDefinitionScanner
2.8)、分析出扫描的包路径数组basePackages,
2.9)、使用ClassPathBeanDefinitionScanner扫描basePackages包中符合条件的Bean注册到容器,然后检查Bean是否为ConfigurationClass,如果是则递归解析。扫描过程:

protected Set doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    //创建一个集合,存放扫描到BeanDefinition
    Set beanDefinitions = new LinkedHashSet();
     //遍历扫描所有给定的包  
    for (String basePackage : basePackages) {
        //调用父类ClassPathScanningCandidateComponentProvider的方法扫描给定类路径,获取符合条件的BeanDefinition
        Set candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 普通的BeanDefinition
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 注解的BeanDefinition
            // 处理注解@Primary、@DependsOn等Bean注解
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 检查候选的,主要是检查BeanFactory中是否包含此BeanDefinition
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

其中findCandidateComponents(basePackage)是调用父类ClassPathScanningCandidateComponentProvider中的方法来获取符合条件的BeanDefinition。这个类在初始化的时候,会注册一些默认的过滤规则,与includeFilters和excludeFilters协调工作来过滤候选BeanDefinition,注册Spring默认规则:

protected void registerDefaultFilters() {
    //  向include过滤规则中添加@Component注解
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 向include过滤规则添加JSR-250:@ManagedBean注解 
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        // 向include过滤规则添加JSR-330:@Named注解  
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

可以看到使用使用Spring @Component注解类、使用JSR-250:@ManagedBean、JSR-330:@Named注解的类都在include规则中,另外Spring中@Repository 、@Service、@Controller、@Configuration都是被@Component注解过的组合注解,所以添加了这些注解的类都会作为候选的Bean。获取候选Bean:

public Set findCandidateComponents(String basePackage) {
    // 创建存储扫描到的类的集合  
    Set candidates = new LinkedHashSet();
    try {
        // 默认的包路径:this.resourcePattern=” **/*.class”,  
        // resolveBasePackage方法将包名中的”.”转换为文件系统的”/”  
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // 循环获取到的资源文件
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    //为指定资源获取元数据读取器,元信息读取器通过汇编(ASM)读//取资源元信息
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    //如果扫描到的类符合容器配置的过滤规则  
                    if (isCandidateComponent(metadataReader)) {
                        //通过汇编(ASM)读取资源字节码中的Bean定义元信息
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        //设置Bean定义来源于resource 
                        sbd.setResource(resource);
                         //为元数据元素设置配置资源对象  
                        sbd.setSource(resource);
                        //检查Bean是否是一个可实例化的对象
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
        // 省略 debug和异常处理的代码 ... ...
    return candidates;
}

2.10)、扫描完成返回beanDefinitions,如果发现beanDefinitions中有Configuration类,进行递归。

step 3 处理@Import注解
通过getImports(sourceClass)获取Configuration类中使用Import注解的类

private Set getImports(SourceClass sourceClass) throws IOException {
    Set imports = new LinkedHashSet();
    Set visited = new LinkedHashSet();
    collectImports(sourceClass, imports, visited);
    return imports;
}

private void collectImports(SourceClass sourceClass, Set imports, Set visited)
        throws IOException {
    if (visited.add(sourceClass)) {
        for (SourceClass annotation : sourceClass.getAnnotations()) {
            String annName = annotation.getMetadata().getClassName();
            if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
                collectImports(annotation, imports, visited);
            }
        }
        imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    }
}

collectImports方法中包含一个递归,如果你使用过Springboot可以会对里面的组合注解的解析有过疑问,其实springboot中组合注解的解析过程就是这个递归的过程:先递归找出所有的注解,然后再过滤出只有@Import注解的类,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解数据的话,首先发现@SpringBootApplication不是一个@Import注解,然后递归调用修饰了@SpringBootApplication的注解,发现有个@EnableAutoConfiguration注解,再次递归发现被@Import(EnableAutoConfigurationImportSelector.class)修饰,还有@AutoConfigurationPackage注解修饰,再次递归@AutoConfigurationPackage注解,发现被@Import(AutoConfigurationPackages.Registrar.class)注解修饰,所以@SpringBootApplication注解对应的@Import注解有2个,分别是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。所以递归的目的就是找出所有的Import类,拿到这个Import列表进行解析过程:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection importCandidates, boolean checkForCircularImports) throws IOException {
    if (importCandidates.isEmpty()) {
        return;
    }
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            // 遍历这些@Import注解内部的属性类集合ComponentScanAnnotationParser
            for (SourceClass candidate : importCandidates) {
                if (candidate.isAssignable(ImportSelector.class)) {//如果这个类是个ImportSelector接口的实现类
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    Class candidateClass = candidate.loadClass();
                    // 实例化这个ImportSelector
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            selector, this.environment, this.resourceLoader, this.registry);
                    // 如果这个类也是DeferredImportSelector接口的实现类,
                    // 那么加入ConfigurationClassParser的deferredImportSelectors
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectors.add(
                                new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }else {
                        // 否则调用ImportSelector的selectImports方法得到需要Import的类
                        // 然后对这些类递归做@Import注解的处理
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                // 如果这个类是ImportBeanDefinitionRegistrar接口的实现类
                // 设置到配置类的importBeanDefinitionRegistrars属性中
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    Class candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                } else {
                    // 其它情况下把这个类入队到ConfigurationClassParser的importStack(队列)属性中
                    // 然后把这个类当成是@Configuration注解修饰的类递归重头开始解析这个类
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    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可以引入普通类、Configuration类、ImportBeanDefinitionRegistrar和ImportSelector的实例,不排斥这些类中也包含Import,所以方法中也包含一个processConfigurationClass递归。
ImportSelector和ImportBeanDefinitionRegistrar是Spring中两个扩展接口,分别通过selectImports方法和registerBeanDefinitions方法向容器注入Bean。
另外ImportSelector还有一个子接口DeferredImportSelectors,这
个接口的实现类会等到Configuration类解析完之后在进行再进行processImports处理。
它们也是springboot加载自动配置的使用的注入方式。

step 4 处理@Bean注解
这个处理过程很容易理解:

// 获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

至此一个基于注解的Configuration类就解析完成了,各个Bean类的BeanDefinition也注册进了容器,等待实例化。

码字不易,转载请保留原文连接https://www.jianshu.com/p/b61809506d0b

你可能感兴趣的:(spring容器加载分析 三Configuration类解析)