spring

spring自动装配

2022-03-06  本文已影响0人  b6d5ffb96342

以前我们使用spring引入一个中间件的客户端,都需要在maven中引入jar包后,再通过代码或者xml配置文件的方式实例化我们所需要的bean。通常我们需要学习如何配置,学习实例化哪些bean,才能让功能正常使用。而通过自动装配的方式,我们只需要引入对应jar包,jar包中的bean会自动被扫描并实例化,用户很简单的就可以开始使用新引入的功能。

自动装配的原理

使用自动装配的第一步,就是在启动类上引入注解@EnableAutoConfiguration

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

可以看到该注解引入了EnableAutoConfigurationImportSelector.class,它继承了AutoConfigurationImportSelector.class,而AutoConfigurationImportSelector的功能是将所有自动化配置加载到容器。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    try {
        //读取classLoader下所有资源的 "META-INF/spring-autoconfigure-metadata.properties"文件,把所有值封装为Properties
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        //读取注解@EnableAutoConfiguration的参数
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //读取classLoader下所有资源的 "META-INF/spring.factories"文件,获取所有要自动装配的类路径
        List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
        //去重
        configurations = removeDuplicates(configurations);
        //排序,spring-autoconfigure-metadata.properties中有定义加载顺序
        configurations = sort(configurations, autoConfigurationMetadata);
        //根据注解的参数,获取不可用的配置,还有配置文件中通过spring.autoconfigure.exclude定义的类
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        //检查哪些排除的类不可用
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //通过配置的AutoConfigurationImportFilter过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        //通过配置的监听器监听自动配置监听事件<OnAutoConfigurationImportEvent>
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return configurations.toArray(new String[configurations.size()]);
    }
    catch (IOException ex) {
        throw new IllegalStateException(ex);
    }
}

我们一步一步看一下过程,首先是AutoConfigurationMetadataLoader如何读取资源。可以看到该过程比较简单,利用jdk的ClassLoader读取资源。

final class AutoConfigurationMetadataLoader {

    protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";

    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, PATH);
    }

    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
        try {
            Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
                : ClassLoader.getSystemResources(path);
            Properties properties = new Properties();
            while (urls.hasMoreElements()) {
                properties.putAll(PropertiesLoaderUtils
                                  .loadProperties(new UrlResource(urls.nextElement())));
            }
            return loadMetadata(properties);
        }
        catch (IOException ex) {
            ...
        }
    }

getCandidateConfigurations方法获取自动配置的类,也是利用classLoader获取

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

我们先看一下spring.factories和spring-autoconfigure-metadata.properties的格式。spring.factories指定了,哪些类会被自动加载;spring-autoconfigure-metadata.properties定了自动配置类被加载的条件和顺序,当然自动配置类本身也定义了条件和顺序

##spring-autoconfigure-metadata.properties
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration.ConditionalOnClass=com.netflix.discovery.EurekaClientConfig
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration.Configuration=

##spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration

接下来看一下排序逻辑,AutoConfigurationSorter负责实际的排序。

class AutoConfigurationSorter {

    private final MetadataReaderFactory metadataReaderFactory;

    private final AutoConfigurationMetadata autoConfigurationMetadata;

    public List<String> getInPriorityOrder(Collection<String> classNames) {
        //AutoConfigurationClass会读取每一个自动配置类的注解,记录下前序类和后序类,还有执行顺序。如果spring-autoconfigure-metadata.properties中定义了顺序则该顺序优先。
        final AutoConfigurationClasses classes = new AutoConfigurationClasses(
                this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
        List<String> orderedClassNames = new ArrayList<String>(classNames);
        // Initially sort alphabetically
        Collections.sort(orderedClassNames);
        // Then sort by order
        Collections.sort(orderedClassNames, new Comparator<String>() {

            @Override
            public int compare(String o1, String o2) {
                int i1 = classes.get(o1).getOrder();
                int i2 = classes.get(o2).getOrder();
                return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
            }

        });
        //通过前面读取出的前序类和后序类,进行排序,里面包含一个递归算法
        orderedClassNames = sortByAnnotation(classes, orderedClassNames);
        return orderedClassNames;
    }
}

看一下过滤的流程

private List<String> filter(List<String> configurations,AutoConfigurationMetadata autoConfigurationMetadata) {
    String[] candidates = configurations.toArray(new String[configurations.size()]);
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    //getAutoConfigurationImportFilters会从spring.factories中读取定义好的AutoConfigurationImportFilter类
    for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
        //如果AutoConfigurationImportFilter实现了BeanClassLoaderAware、BeanFactoryAware等类,会把对应的类写到filter里
        invokeAwareMethods(filter);
        //过滤
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);
        for (int i = 0; i < match.length; i++) {
            if (!match[i]) {
                skip[i] = true;
                skipped = true;
            }
        }
    }
    if (!skipped) {
        return configurations;
    }
    List<String> result = new ArrayList<String>(candidates.length);
    for (int i = 0; i < candidates.length; i++) {
        if (!skip[i]) {
            result.add(candidates[i]);
        }
    }
    return new ArrayList<String>(result);
}

监听器的执行和过滤的流程类似,全部执行完之后,需要自动注入的类名单就交给容器去处理了。之后需要了解bean的加载,自动配置和@configuration的顺序,如何才能控制哪些类不加载

上一篇 下一篇

猜你喜欢

热点阅读