zoukankan      html  css  js  c++  java
  • 我是如何做到springboot自动配置原理解析

    摘自:https://www.cnblogs.com/zszxz/p/12195703.html

    一前言

    springboot 2.0.0版本分析,整体的自动配置流程如下:

    在这里插入图片描述

    具体配置参考官方文档:springboot-doc

    二 @SpringBootApplication

    核心注解@SpringBootConfiguration其实就是@Configuration注解,表示是个配置类;@EnableAutoConfiguration表示springboot的自动配置机制;@ComponentScan表示扫描允许注册额外的配置类;

    @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

    Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    
        // 排除不会被应用的自动配置类,classes形式
        @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
        Class<?>[] exclude() default {};
    
        // 排除不会被应用的自动配置类,字符串数组形式
        @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
        String[] excludeName() default {};
    
        // 扫描基本包
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};
    
        // 扫描基本类
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};
    
    }

    三 AutoConfigurationImportSelector

    点击@EnableAutoConfiguration 注解进入,看见@Import({AutoConfigurationImportSelector.class})是导入AutoConfigurationImportSelector类;

    AutoConfigurationImportSelector 类是自动配置的核心类,其主要进行配置的功能是配置factory.properties和 spring内嵌集成引入的配置;

    3.1 具体依赖图如下:

    在这里插入图片描述

    3.2 成员

        // 表示不引入配置
        private static final String[] NO_IMPORTS = new String[0];
        // 配置日志
        private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
        // 排除的自动配置
        private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
        // 声明 beanFactory
        private ConfigurableListableBeanFactory beanFactory;
        // 声明 environment (全局环境)
        private Environment environment;
        // 声明 beanClassLoader (bean的类加载器,加载spring-autoconfigure-metadata.properties中集成类)
        private ClassLoader beanClassLoader;
        // 声明 resourceLoader (资源加载器,加载spring的 factory.properties配置类)
        private ResourceLoader resourceLoader;

    3.3 selectImports

    selectImports 这个方法的主要功能就是导入factory.properties和 spring-autoconfigure-metadata.properties中的配置类;

     public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                try {
                  // 1 加载元数据信息,本质就是加载bean,这里的bean是指我我们spring-autoconfigure-metadata.properties中集成类
                    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                    // 2 AnnotationAttributes本质是个map
                    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                    // 3  获得 spring.factories 中的配置类
                    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                    // 4  配置类复制用于排序不需要的配置类
                    configurations = this.removeDuplicates(configurations);
                    //  优先级排序
                    configurations = this.sort(configurations, autoConfigurationMetadata);
                    // 需移除配置类
                    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                    // 校验移除
                    this.checkExcludedClasses(configurations, exclusions);
                    // 执行移除配置类
                    configurations.removeAll(exclusions);
                    configurations = this.filter(configurations, autoConfigurationMetadata);
                    this.fireAutoConfigurationImportEvents(configurations, exclusions);
                    return StringUtils.toStringArray(configurations);
                } catch (IOException var6) {
                    throw new IllegalStateException(var6);
                }
            }
        }
    

    1 分析
    AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

    进入方法发现加载的元数据信息的路径是 META-INF/spring-autoconfigure-metadata.properties

    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
            return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
        }

    在自动配置包底下找到spring-autoconfigure-metadata.properties

    在这里插入图片描述
    点进属性文件发现都spring自动配置类名的配置信息,部分如下:

    #Thu Mar 01 04:46:13 UTC 2018
    org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
    org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration.Configuration=
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Flux
    org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
    ............................

    我们随意点开其中的一个配置类,比如第一个HttpMessageConvertersAutoConfiguration,其中的class注解如下,可以发现其自动配置类都是通过注解配置;

    // 表示配置类相当于xml中的 bean标签
    @Configuration
    // 判定是否存在HttpMessageConverter.class类,如果不存在则引入
    @ConditionalOnClass({HttpMessageConverter.class})
    // 在这三个配置类配置之后再进行配置
    @AutoConfigureAfter({GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class})
    // 导入配置类相当于xml中的 import标签
    @Import({JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class, JsonbHttpMessageConvertersConfiguration.class})
    public class HttpMessageConvertersAutoConfiguration {

    3 分析

    this.getCandidateConfigurations(annotationMetadata, attributes);方法的主要功能是获取候选配置;

    进入getCandidateConfigurations方法发现里面的主要方法是loadFactoryNames;

    // 获得factory配置信息类名
     protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
        }

    进入 loadFactoryNames 方法 主要是2部分;第一个是loadSpringFactories(classLoader),第二个是getOrDefault

    // 加载factory类名
     public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            //  返回类名的list
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }

    第一部分:
    loadSpringFactories本质就是使用spring的Resource资源调用获得 spring.factories 中的配置类;

    // 加载spring.factories中的配置类信息
    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 {
                    // 获得 `spring.factories` 配置类的URL
                    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                    LinkedMultiValueMap result = new LinkedMultiValueMap();
                    // 将 `spring.factories`  每个配置类的key 和 val 存储进 map
                    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);
                }
            }
        }
    

    classLoader.getResources("META-INF/spring.factories") ;中找到自动配置包中的配置如下图:
    在这里插入图片描述

    spring.factories装载配置类部分信息如下,没错这些配置都是sping启动需要的配置类信息,监听器,过滤器,自动配置的start配置类,以及启动的失败的错误分析还有模板引擎的支持,详细大家翻下配置包即可;

    # Initializers
    org.springframework.context.ApplicationContextInitializer=
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
    org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    ....................................
    

    第二部分:
    getOrDefault是个Map集合,map中有这个key时,就使用这个key值,如果没有就使用默认值defaultValue,返回也就是类名的list;

    在这里插入图片描述

     
    分类: springboot
  • 相关阅读:
    java基础知识回顾之---java String final类构造方法
    java基础知识回顾之---java String final类普通方法
    递归的理解
    跟着牛人学习
    【轻松学排序算法】眼睛直观感受几种常用排序算法
    《PhotoShop CS6 》第一节 矢量与分辨率
    SQL 查询当天,本月,本周的记录
    SQL创建表脚本
    varchar和Nvarchar区别
    [C#] 我的log4net使用手册
  • 原文地址:https://www.cnblogs.com/xichji/p/12208587.html
Copyright © 2011-2022 走看看