spring boot自动装配源码分析

2020-05-18  本文已影响0人  Gorden_Tam

@Import注解

Import注解可以导入一个class,该class可以为一个被Configuration注解的配置类,或者一个实现ImportSelector或者ImportBeanDefinitionRegustrar的类。关于import注解的解析在Spring的ConfigurationClassParser类中。

@Import
@Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar

1.configuration,导入该配置类的全部bean。

2.ImportSelector

public interface ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
     //入参importingClassMetadata为使用最上层配置类对应的AnnotationMetadata,比如@Configuration @Enable*** AnnotationMetadata为@Configuration类而不是@Enable**注解对象的类。
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

应用上下文将会导入String数组中的类的全限定名指向的Bean

3.ImportBeanDefinitionRegistrar

public interface ImportBeanDefinitionRegistrar {

    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing {@code @Configuration} class.
     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
     * registered here, due to lifecycle constraints related to {@code @Configuration}
     * class processing.
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry current bean definition registry
     */
     //importingClassMetadata最开始的导入ImportBeanDefinitionRegistrar的配置类
     //然后可以将BeanDefinition注册到入参registry中
    void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

@Import注解是在应用上下文refresh刷新时,调用BeanFactoryPostProcessor处理器时,调用spring自带的ConfigurationClassPostProcessor时解析的,具体解析过程在ConfigurationClassParser中。
@Enablexxx就是依赖@import注解和@Conditional注解实现的。
ConfigurationClassParser的doProcessConfigurationClass方法中有processImports实现

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

    /**
     * Recursively collect all declared {@code @Import} values. Unlike most
     * meta-annotations it is valid to have several {@code @Import}s declared with
     * different values; the usual process of returning values from the first
     * meta-annotation on a class is not sufficient.
     * <p>For example, it is common for a {@code @Configuration} class to declare direct
     * {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
     * annotation.
     * @param sourceClass the class to search
     * @param imports the imports collected so far
     * @param visited used to track visited classes to prevent infinite recursion
     * @throws IOException if there is any problem reading metadata from the named class
     */
    //这里就是一个递归,递归解析所有非jvm的注解,收集import注解
    private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
            throws IOException {

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

首先递归获取入口配置类下相关联的所有使用使用@Import注解的类(或者注解),获取其Import的class转换成SourceClass对象并加入visited集合,SourseClass其实是对class对象的包装,

    //简单包装一个被注解注释的类,而不用考虑他是如何被加载的
    private class SourceClass implements Ordered {

        private final Object source;  // Class or MetadataReader

        private final AnnotationMetadata metadata;

可以获取某个class的所有注解元数据AnnotationMetadata。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

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

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) {
                    //如果是importSelector的实现的话
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to it to determine imports
                        //实例化
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        //如果selector如果也继承了environmentAware resourceLoaderAware等接口的话,则调用这些接口
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, this.registry);
                        if (selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                        }
                        else {
                            //调用selector.selectImport 参数为当前sourceClass
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, importSourceClasses, false);
                        }
                    }
                    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);
                        // 将ImportBeanDefinitionRegistrar实例添加到ConfigurationClass中去
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {
                        // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        // 如果不是importSelector或者实现类,则将其作为ConfigurationClass解析
                        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();
            }
        }
    }

然后依次遍历这些SourceClass,判断是不是ImportBeanDefinitionRegistrar或者ImportSelector的实现,如果不是,则将其作为配置类处理。最后所有这些导入的都转换为ConfigurationClass对象被ConfigurationClassPostProcessor解析。

spring boot自动装配原理:

@SpringBootApplication注解标记一个应用为springboot应用,其由三个注解组成,分别为为@EnableAutoConfiguration、@ComponentScan和@Configuration。@ComponentScan注解可以将同一包中所有配置类的bean引入,而对于其他包,除非显示通过@ComponentScan配置,否则其配置类中的Bean无法引入到。不过通过@EnableAutoConfiguration注解,将这些配置类及其包的位置加入到classpath的META-INF/spring.factories中,再配合上@ConditionalClass注解,则可以只要再maven中引入,classpath中存在某类,即可注入这些类对应的Bean.@EnableAutoConfiguration作用只不过是收集注册特定场景相关bean。
一般另一种引入其他包配置类的方式为自定义@Enable***注解比如@EnableDiscoveryClient

@EnableAutoConfiguration原理:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

实际上就是import了一个ImportSelector,不过只有spring.boot.enableautoconfiguration为true的时候才启用自动装配。

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

    /**
     * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
     * of the importing {@link Configuration @Configuration} class.
     * @param autoConfigurationMetadata the auto-configuration metadata
     * @param annotationMetadata the annotation metadata of the configuration class
     * @return the auto-configurations that should be imported
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        //判断spring.boot.enableautoconfiguration是否为true
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        //这里会排出@EnableAutoConfiguration中exclude()的那些类,默认一个也不exclude
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

在getCandidateConfigurations方法中,会调用getCandidateConfigurations方法。getCandidateConfigurations方法中通过调用SpringFactoriesLoader的loadFactoryNames方法,在classpath中搜索所有META-INF/spring.factories文件并将其中预先配置的所有类的全限定名取出,最后会将这些类注册为Bean。

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

spring boot的自动装配一般搭配@ConditionalClass或者@ConditionalOnMissingBean注解使用。
所以当spring boot引入一个maven依赖时,如果该依赖的jar包下面的META-INF/spring.factories配置了org.springframework.boot.autoconfigure.EnableAutoConfiguration,则这些类会自动生成bean加载到应用上下文中。

上一篇 下一篇

猜你喜欢

热点阅读