美文网首页Java编程SpringBoot
源码学习系列之SpringBoot自动配置(篇二)

源码学习系列之SpringBoot自动配置(篇二)

作者: smileNicky | 来源:发表于2019-11-17 12:36 被阅读0次

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析

    继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续跟一下SpringBoot的自动配置源码

    ok,先复习一下上一篇的内容,从前面的学习,我们知道了SpringBoot的自动配置主要是由一个选择器AutoConfigurationImportSelector,先通过选择器将自动配置的类加载到Spring容器

    注意点:

    • List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取的候选配置的类名
    • 由SpringFactoriesLoader加载器负责加载配置类名,已经装载配置类到容器,SpringFactoriesLoader的loadSpringFactories方法读取自动配置工程的META-INF/spring.factories配置文件,加载配置类的全类名,包装成Properties对象,然后再加载到容器里
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        /* 将spring.factories的类都装载到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
            //将META-INF/spring.factories文件里配置的属性都装载到Enumeration数据结构里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
                //遍历获取属性,然后再获取对应的配置类全类名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    
    

    ok,Springboot的自动配置类都在这个包里,源码很多,所以本博客只是简单跟一下源码

    在这里插入图片描述
    自动配置可以说是SpringBoot框架的一个很重要的功能,其强大的功能就是通过很多配置类实现的,当然这么多配置,可以参考SpringBoot官方的配置参考:
    https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/common-application-properties.html

    SpringBoot的自动配置类很多,显然不是每个配置类都生效的,比如你没引对应的jar,那对应的配置类肯定是不起效的,ok,本博客以HttpEncodingAutoConfiguration自动编码配置类为实例,记录一下SpringBoot的自动配置

    先补充一些@Conditional注解的用法:详情可以参考我上篇博客SpringBoot系列之@Conditional注解用法简介

    @Conditional派生注解 作用(都是判断是否符合指定的条件)
    @ConditionalOnJava 系统的java版本是否符合要求
    @ConditionalOnBean 有指定的Bean类
    @ConditionalOnMissingBean 没有指定的bean类
    @ConditionalOnExpression 符合指定的SpEL表达式
    @ConditionalOnClass 有指定的类
    @ConditionalOnMissingClass 没有指定的类
    @ConditionalOnSingleCandidate 容器只有一个指定的bean,或者这个bean是首选bean
    @ConditionalOnProperty 指定的property属性有指定的值
    @ConditionalOnResource 路径下存在指定的资源
    @ConditionalOnWebApplication 系统环境是web环境
    @ConditionalOnNotWebApplication 系统环境不是web环境
    @ConditionalOnjndi JNDI存在指定的项

    通过上篇博客的学习,我们已经知道了SpringBoot有很多自动配置类,所以本博客拿HttpEncodingAutoConfiguration类来看看

    补充:

    • @Configuration proxyBeanMethods属性:默认是开启的,开启后允许其它配置类调用这个类的@bean方法,详情参看Spring官方文档:Spring官方文档
    package org.springframework.boot.autoconfigure.web.servlet;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
    import org.springframework.boot.autoconfigure.http.HttpProperties;
    import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
    import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.filter.CharacterEncodingFilter;
    
    @Configuration(
        proxyBeanMethods = false
    )//指定是一个配置列,关了proxyBeanMethods,其它配置类就不能调相应的@bean类
    @EnableConfigurationProperties({HttpProperties.class})//让使用 @ConfigurationProperties注解的HttpProperties类生效,HttpProperties类通过ConfigurationProperties注解,将属性配置一个一个加载进来
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//指定系统环境是Web环境配置才起效,并且指定类型是SERVLET
    @ConditionalOnClass({CharacterEncodingFilter.class})//系统有CharacterEncodingFilter过滤器类,则配置类起效,CharacterEncodingFilter类:SpringMVC中进行乱码解决的过滤器
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//判断配置文件是否有spring.http.encoding.enabled属性,如果没配置,也是默认为true的,因为配置了`matchIfMissing =true`
    public class HttpEncodingAutoConfiguration {
        //创建一个Encoding对象
        private final Encoding properties;
        // 构造函数里通过properties.getEncoding();进行属性映射,获取默认的配置
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        @Bean
        @ConditionalOnMissingBean//如果系统没有CharacterEncodingFilter类,就执行characterEncodingFilter方法
        public CharacterEncodingFilter characterEncodingFilter() {
            //重新创建一个编码过滤器
            OrderedCharacterEncodingFilter 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;
            }
        }
    }
    
    

    通过对HttpEncodingAutoConfiguration源码的学习,可以看出,其实主要是用@Conditional及其派生注解,这些注解都是要在特定情况才会起效,起效了,才会将组件加载到Spring容器里

    ok,然后我们怎么知道哪些配置是起效的?在SpringBoot项目里,是可以通过配置,开启打印的,可以在application.properties加上debug=true属性就可以

    控制台打印的Positive matches就表示有效的配置类


    在这里插入图片描述

    console打印的Negative matches表示不起效的配置类:
    比如我的项目没有加aop的,aop自动配置类就不起效


    在这里插入图片描述

    相关文章

      网友评论

        本文标题:源码学习系列之SpringBoot自动配置(篇二)

        本文链接:https://www.haomeiwen.com/subject/qvjpictx.html