美文网首页spring boot解析
SpringBoot系列二:SpringBoot自动配置原理

SpringBoot系列二:SpringBoot自动配置原理

作者: 小波同学 | 来源:发表于2018-12-29 19:21 被阅读2次

    原文出处: 晴枫

    1 SpringBoot运作原理

    上一章中我们提到主程序类的注解 @SpringBootApplication 注解,它其实是个组合注解,源码如下:

    @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 {
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        Class<?>[] exclude() default {};
     
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        String[] excludeName() default {};
     
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackages"
        )
        String[] scanBasePackages() default {};
     
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackageClasses"
        )
        Class<?>[] scanBasePackageClasses() default {};
    }
    

    最主要的还是三个配置 @SpringBootConfiguration、@EnableAutoConfigration、@ComponentScan 三个注解,下面我们来一一分析。

    1.1 @SpringBootConfiguration

    查看@SpringBootConfiguration源码,其实它也就是@Configuration注解:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    }
    

    对于 @Configuration,我们并不陌生,它就是 JavaConfig 形式的 SpringIOC 容器的配置类,也是 SpringBoot 推荐使用配置形式,所以主程序类标注了 @SpringBootConfiguration 注解,其本身也就是一个配置类。更多有关 JavaConfig 形式的配置以及有关它和 XML 形式的配置的区别与联系,请参考后续有关 Spring 注解版相关文章。

    1.2 @ComponentScan

    @ComponentScan 注解在 Spring 的注解中也起到到相当重要的作用,它可以自定义 Spring 扫描的包,也就是它默认会扫描标注了 @Controller、@Service、@Component 以及 @Repository 注解的类,并实例化这些组件到 SpringIOC 容器中,它有个配置属性: basePackages,也就是指定扫描的包,如果不知道,它会默认扫描配置了该注解的类的包所在的路径(包括子包)。我们看 @SpringBootConfiguration 注解的源码中有段代码:

    @AliasFor(
      annotation = ComponentScan.class,
      attribute = "basePackages"
    )
    String[] scanBasePackages() default {};
    

    scanBasePackages 属性,指定到了 @ComponentScan 注解的 basePackages 属性,所有在 SpringBoot 中,我们同样可以通过 scanBasePackages 属性指定包扫描的路径(如部指定,会默认扫描主程序类所在的包路径以及子包下的类):

    @SpringBootApplication(scanBasePackages = "com.seagetech.springbootdemo")
    

    1.3 @EnableAutoConfigration

    关于 SpringBoot 的运作原理,它的核心功能还是由 @EnableAutoConfigration 注解提供,所有把它放到最后来讲,我们来看下它的源码:

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

    1.3.1 @AutoConfigurationPackage

    这个注解的主要功能自动配置包,它会获取主程序类所在的包路径,并将包路径(包括子包)下的所有组件注册到 SpringIOC 容器中。

    1.3.2 @Import({AutoConfigurationImportSelector.class})

    @EnableAutoConfiguration 的关键功能也是这个 @Import 导入的配置功能,使用 SpringFactoriesLoader.loadFactoryNames 方法来扫描具有 META-INF/spring.factories 文件的jar包,我们看看在 spring-boot-autoconfigure-2.10.RELEASE.jar 包的 META-INF 下正好有个 spring.factories 文件:

    image

    打开 spring.factories 文件,找到 org.springframework.boot.autoconfigure.EnableAutoConfiguration 指定的自动配置类:

    # Auto Configure
    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.web.client.RestTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
    

    每一个 xxxAutoConfiguration 类都是容器中的一个组件,都加入到容器中,用它们来做自动配置。

    2 自动配置分析

    在上一节基础上,我们知道每一个自动配置类进行自动配置功能,下面我们以 HttpEncodingAutoConfiguration 为例来分析:

    2.1 配置参数

    @ConfigurationProperties(
        prefix = "spring.http"//①
    )
    public class HttpProperties {
        private boolean logRequestDetails;
        private final HttpProperties.Encoding encoding = new HttpProperties.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;//②
            }
            ......
            static {
                DEFAULT_CHARSET = StandardCharsets.UTF_8;//②
            }
            ......
    }
    

    代码解析:

    1. 在 application.properties 文件中配置的时候指定前缀是 spring.http;
    2. 指定默认的编码方式是 UTF-8,如果需要修改,可使用 spring.http.encoding.charset=编码。

    2.2 自动配置类

    @Configuration//①
    @EnableConfigurationProperties({HttpProperties.class})//②
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//③
    @ConditionalOnClass({CharacterEncodingFilter.class})//④
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//⑤
    public class HttpEncodingAutoConfiguration {
        private final Encoding properties;
     
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
     
        @Bean
        @ConditionalOnMissingBean
        public CharacterEncodingFilter characterEncodingFilter() {
            ......
        }
        .......
    }
    

    源码解析:

    1. 表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件。
    2. 启动指定类的 ConfigurationProperties 功能,将配置文件中对应的值和 HttpEncodingProperties 绑定起来;并把 HttpEncodingProperties 加入到 IOC 容器中;
    3. Spring 底层 @Conditional 注解(有关 @Conditional 注解的详解请参考后续 Spring 注解相关文章),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效,该注解是判断当前应用是否是web应用,如果是, HttpEncodingAutoConfiguration 配置类生效;
    4. 判断当前项目有没有这个类 CharacterEncodingFilter;SpringMVC 中进行乱码解决的过滤器,如果有则 HttpEncodingAutoConfiguration 配置生效;
    5. 判断 application.properties 配置文件中是否存在 spring.http.encoding.enabled,如果不存在,判断也是成立的,因为 matchIfMissing=true,即缺省时默认为 true。

    根据当前不同的条件判断,决定 HttpEncodingAutoConfiguration 这个配置类是否生效?一但这个配置类生效;这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的 HttpEncodingProperties 类中获取的,这些类里面的每一个属性又是和配置文件绑定的。

    2.3 @Conditional扩展注解

    除了以上解析到的注解,SpringBoot 还为我们提供了更多的有关 @Conditional 的派生注解。它们的作用:必须是 @Conditional 指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效:

    image

    相关文章

      网友评论

        本文标题:SpringBoot系列二:SpringBoot自动配置原理

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