8_28 springboot 为什么在application.xml(或者application.yml ) 中的配置可以生效

  1. 首先看主配置类中的SpringBootApplication注解,进点去
    @SpringBootApplication
    public class RestfulcrudApplication {
        public static void main(String[] args) {
            SpringApplication.run(RestfulcrudApplication.class, args);
        }
    }

    可以看到一堆注解,其中这个EnableAutoConfiguration就是用来开启自动配置的,点进去

    @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 {
    }
    可以看到这个注解注解导入了 AutoConfigurationImportSelector.class,这个类就是用来自动配置的,点进去
    @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 {};
    }

    AutoConfigurationImportSelector.class中的 selectImports(AnnotationMetadata annotationMetadata)方法调用了getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)方法,而getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)方法调用了getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,

       public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
        }
    
        protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            } else {
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
            }
        }

    getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,调用了loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); 要想知道为什么可以自动配置,得先从loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())方法看起

    
        public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }

     

  2. 把断点打在loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())方法上,开启debug模式

     public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    //loadFactoryNames 首先获取了一个工厂bean,传给了loadSpringFactories
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }

    在进入loadSpringFactories 方法之前要先看一下 jar 包下的META-INF 下的 spring.factories,它在这里,                                                                                        里边的内容是

    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=\
    org.springframework.boot.env.PropertiesPropertySourceLoader,\
    org.springframework.boot.env.YamlPropertySourceLoader
    
    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    
    # Error Reporters
    org.springframework.boot.SpringBootExceptionReporter=\
    org.springframework.boot.diagnostics.FailureAnalyzers
    
    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    
    # Environment Post Processors
    org.springframework.boot.env.EnvironmentPostProcessor=\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
    org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
    
    # Failure Analyzers
    org.springframework.boot.diagnostics.FailureAnalyzer=\
    org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
    
    # FailureAnalysisReporters
    org.springframework.boot.diagnostics.FailureAnalysisReporter=\
    org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
    

       进入loadSpringFactories 方法

     private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            //实例化 一个一对多的键值对map
            MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
            if (result != null) {
                return result;
            } else {
                try {
            //从这里开始就是重点了, urls 中保存的是各个 jar 包下的 META-INF/spring.factories 中 “每一段” 的第一行
                    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            //创建一个一对多的map,就是一个键可以对应多个值
                    LinkedMultiValueMap result = new LinkedMultiValueMap();
          //这个 while 是遍历I  spring.factories 中的每一个“部分”,因为urls 存的是每一个“部分”的第一行
                    while(urls.hasMoreElements()) {
                        URL url = (URL)urls.nextElement();
                        UrlResource resource = new UrlResource(url);
          //把url 封装成一个 Properties  对象,这个Properties  对象对象是很多个 spring.factories“部分”
                        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         //获得Properties  对象的迭代器,这里迭代的就是一个一个“部分”
                        Iterator var6 = properties.entrySet().iterator();
                        while(var6.hasNext()) {
        //创建一个Entry 对象, 这个对象的键将存者每一个部分的第一行,值存的是每一个“部分”除了第一行之外的部分
                            Entry<?, ?> entry = (Entry)var6.next();
                            String factoryClassName = ((String)entry.getKey()).trim();
        //将值的部分转换成一个数组
                            String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                            int var10 = var9.length;
                            for(int var11 = 0; var11 < var10; ++var11) {
                                String factoryName = var9[var11];
                                //就是将Entry 对象进行一次转化,使得值部分从一个字符串变成一个数组
                                result.add(factoryClassName, factoryName.trim());
                            }
                        }
                    }
    
                    cache.put(classLoader, result);
                    //返回result
                    return result;
                } catch (IOException var13) {
                    throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
                }
            }
        }

    然后,所有的META-INF/spring.factories下边对应的类,也就是xxxAutoConfiguration就会通过反射的方式实例化并且添加进容器了

  3. 然后,看看这些xxxAutoConfiguration是怎么是怎么工作的,看一个HttpEncodingAutoConfiguration

     
    
    @Configuration
    @EnableConfigurationProperties({HttpProperties.class})
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )
    // CharacterEncodingFilter.class位于类路径上,才会实例化本类
    @ConditionalOnClass({CharacterEncodingFilter.class})
    //这个注解就是说和配置文件中如果 spring.http.encoding 那一项绑定,配置文件中不写的话 默认值为true
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )
    public class HttpEncodingAutoConfiguration {
        private final Encoding properties;
    
    //这个类之后一个有参构造器,也就是说实例化对象的时候会从容器中拿到 HttpProperties  的一个实例,进去
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
    
        /**
    	下边就是这个类的具体功能了
       **/
        @Bean
        @ConditionalOnMissingBean
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
            private final Encoding properties;
    
            LocaleCharsetMappingsCustomizer(Encoding properties) {
                this.properties = properties;
            }
    
            public void customize(ConfigurableServletWebServerFactory factory) {
                if (this.properties.getMapping() != null) {
                    factory.setLocaleCharsetMappings(this.properties.getMapping());
                }
    
            }
    
            public int getOrder() {
                return 0;
            }
        }
    }
    
     那么就不得不看看 HttpProperties.class
     
    //终于找到这个了!!!!!!!!!
    //这个类会和配置文件中的 spring.http.xxx = xxx 的属性绑定,也就是说在配置文件中配置的  spring.http.xxx = xxx 会被作为实例化本类的属性
    @ConfigurationProperties(
        prefix = "spring.http"
    )
    public class HttpProperties {
        private boolean logRequestDetails;
        private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();
    
        public HttpProperties() {
        }
    
        public boolean isLogRequestDetails() {
            return this.logRequestDetails;
        }
    
        public void setLogRequestDetails(boolean logRequestDetails) {
            this.logRequestDetails = logRequestDetails;
        }
    
        public HttpProperties.Encoding getEncoding() {
            return this.encoding;
        }
    
        public static class Encoding {
            public static final Charset DEFAULT_CHARSET;
            private Charset charset;
            private Boolean force;
            private Boolean forceRequest;
            private Boolean forceResponse;
            private Map<Locale, Charset> mapping;
    
            public Encoding() {
                this.charset = DEFAULT_CHARSET;
            }
    
            public Charset getCharset() {
                return this.charset;
            }
    
            public void setCharset(Charset charset) {
                this.charset = charset;
            }
    
            public boolean isForce() {
                return Boolean.TRUE.equals(this.force);
            }
    
            public void setForce(boolean force) {
                this.force = force;
            }
    
            public boolean isForceRequest() {
                return Boolean.TRUE.equals(this.forceRequest);
            }
    
            public void setForceRequest(boolean forceRequest) {
                this.forceRequest = forceRequest;
            }
    
            public boolean isForceResponse() {
                return Boolean.TRUE.equals(this.forceResponse);
            }
    
            public void setForceResponse(boolean forceResponse) {
                this.forceResponse = forceResponse;
            }
    
            public Map<Locale, Charset> getMapping() {
                return this.mapping;
            }
    
            public void setMapping(Map<Locale, Charset> mapping) {
                this.mapping = mapping;
            }
    
            public boolean shouldForce(HttpProperties.Encoding.Type type) {
                Boolean force = type != HttpProperties.Encoding.Type.REQUEST ? this.forceResponse : this.forceRequest;
                if (force == null) {
                    force = this.force;
                }
    
                if (force == null) {
                    force = type == HttpProperties.Encoding.Type.REQUEST;
                }
    
                return force;
            }
    
            static {
                DEFAULT_CHARSET = StandardCharsets.UTF_8;
            }
    
            public static enum Type {
                REQUEST,
                RESPONSE;
    
                private Type() {
                }
            }
        }
    }
    

     

  4. 到了这里,就可以总结了:

    1. 首先EnableAutoConfiguration注解使得 spring.factories 中的类会被加载容器,说白了就是会实例化一个对象并把这个对象添加到Spring的某个map中,

    2. 要实例化这些对象的时候它们要要求从容器中拿到另外要给xxxProperties类队形作为参数传进去他们的构造函数,为这个类的变量赋值

    3. 而这些xxxProperties是和 配置文件中某个特定的配置绑定的,例如上边的HttpProperties就和配置文件中的spring.http为前缀的配置绑定,这样子在实例化的时候就会把这些配置作为属性赋值。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值