zoukankan      html  css  js  c++  java
  • Springboot 自动装配置

      Spring Boot 相对于传统的Spring引入了自动配置功能,简化了项目中繁琐的配置,让开发者利用起来更加的简便、快捷。比如内嵌的tomcat容器等,这些都属于Spring Boot自动配置的范畴。

      其中@EnableAutoConfiguration注解下的AutoConfigurationImportSelector类就是自动装配的核心。在2.0.6.RELEASE版本中没有getAutoConfigurationEntry方法,在2.1.4.RELEASE版本对其做了优化变成了我们现在看到的样子。

    @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) {
        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);
        //对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常
        checkExcludedClasses(configurations, exclusions);
        //移除exclusions
        configurations.removeAll(exclusions);
        //根据maven导入的启动器过滤出 需要导入的配置类
        configurations = filter(configurations, autoConfigurationMetadata);
        //配置监听事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    1、其中AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader),会加载META-INF/spring-autoconfigure-metadata.properties下的所有配置信息。

    final class AutoConfigurationMetadataLoader {
    
        protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
    
        private AutoConfigurationMetadataLoader() {
        }
    
        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) {
                throw new IllegalArgumentException(
                        "Unable to load @ConditionalOnClass location [" + path + "]", ex);
            }
        }
    }

    2. getCandidateConfigurations(annotationMetadata,attributes),会加载所有包下META-INF/spring.factories的信息并组装成Map,然后读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的数组,并将这个数组返回。

    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;
    }
    # Auto configure从spring.factories中读取到的文件内容
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
    org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,

    3. removeDuplicates(configurations),去除重复的配置类,若我们自己写的starter 可能存主重复的。

    protected final <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }

    4. getExclusions(annotationMetadata, attributes),获取限制候选配置的所有排除项(找到不希望自动装配的配置类)。

    /**
     * Return any exclusions that limit the candidate configurations.
     * @param metadata the source metadata
     * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
     * attributes}
     * @return exclusions or an empty set
     */
    protected Set<String> getExclusions(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet<>();
        excluded.addAll(asList(attributes, "exclude"));
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        excluded.addAll(getExcludeAutoConfigurationsProperty());
        return excluded;
    }
    
    private List<String> getExcludeAutoConfigurationsProperty() {
        if (getEnvironment() instanceof ConfigurableEnvironment) {
            Binder binder = Binder.get(getEnvironment());
            return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
                    .map(Arrays::asList).orElse(Collections.emptyList());
        }
        String[] excludes = getEnvironment()
                .getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
        return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
    }

    5.checkExcludedClasses(configurations, exclusions),对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常。

    private void checkExcludedClasses(List<String> configurations,
            Set<String> exclusions) {
        List<String> invalidExcludes = new ArrayList<>(exclusions.size());
        for (String exclusion : exclusions) {
            if (ClassUtils.isPresent(exclusion, getClass().getClassLoader())
                    && !configurations.contains(exclusion)) {
                invalidExcludes.add(exclusion);
            }
        }
        if (!invalidExcludes.isEmpty()) {
            handleInvalidExcludes(invalidExcludes);
        }
    }
    
    /**
     * Handle any invalid excludes that have been specified.
     * @param invalidExcludes the list of invalid excludes (will always have at least one
     * element)
     */
    protected void handleInvalidExcludes(List<String> invalidExcludes) {
        StringBuilder message = new StringBuilder();
        for (String exclude : invalidExcludes) {
            message.append("	- ").append(exclude).append(String.format("%n"));
        }
        throw new IllegalStateException(String
                .format("The following classes could not be excluded because they are"
                        + " not auto-configuration classes:%n%s", message));
    }

    6. filter(configurations, autoConfigurationMetadata),根据项目中的AutoConfigurationImportFilter类进行过滤
    在项目中找到所有AutoConfigurationImportFilter类进行过滤,对于自动配置类,只要其不满足任意一个filter的match方法,就将其进行过滤,不再自动配置。

    private List<String> filter(List<String> configurations,
                AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            invokeAwareMethods(filter);
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    skip[i] = true;
                    candidates[i] = null;
                    skipped = true;
                }
            }
        }
        if (!skipped) {
            return configurations;
        }
        List<String> result = new ArrayList<>(candidates.length);
        for (int i = 0; i < candidates.length; i++) {
            if (!skip[i]) {
                result.add(candidates[i]);
            }
        }
        if (logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                    + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
                    + " ms");
        }
        return new ArrayList<>(result);
    }

    至此,自动配置的逻辑实现。我们通过RedisAutoConfiguration为例,完整的展示一遍整个流程:

    @Configuration //标识是一个配置类
    @ConditionalOnClass(RedisOperations.class) //判断环境中是否有这个类
    @EnableConfigurationProperties(RedisProperties.class) //启动指定类的配置功能,并且把配置文件中的属性和RedisProperties关联
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) 
    public class RedisAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            RedisTemplate<Object, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    
        @Bean
        @ConditionalOnMissingBean
        public StringRedisTemplate stringRedisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;

    具体的实现流程如图:

    # Auto Configure
  • 相关阅读:
    react引用antd的form表单
    前端学习之--谷歌浏览器使用
    react引用ant的table组件
    git 提交解决冲突
    git将本地仓库推送到远程仓库
    操作DOM
    javaScript基础篇之数据类型
    css之水平居中设置
    css之颜色值、单位
    CSS属性简写
  • 原文地址:https://www.cnblogs.com/jiangyaxiong1990/p/12348360.html
Copyright © 2011-2022 走看看