美文网首页springboot
Springboot 系列(三)Spring Boot 自动配置

Springboot 系列(三)Spring Boot 自动配置

作者: 雪漫士兵 | 来源:发表于2019-02-26 21:50 被阅读5次

    注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别。

    前言

    关于配置文件可以配置的内容,在 Spring Boot 官方网站已经提供了完整了配置示例和解释。

    可以这么说,Spring Boot 的一大精髓就是自动配置,为开发省去了大量的配置时间,可以更快的融入业务逻辑的开发,那么自动配置是怎么实现的呢?

    1. @SpringBootApplication

    跟着 Spring Boot 的启动类的注解 @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 {
    

    @EnableAutoConfiguration 开启自动配置。

    @ComponentScan 开启注解扫描

    SpringBootApplication 我们可以发现,这是一个简便的注解配置,它包含了自动配置,配置类,包扫描等一系列功能。

    2. @EnableAutoConfiguration

    继续跟踪,查看@EnableAutoConfiguration 源码,里面比较重要的是 @Import ,导入了一个翻译名为自动配置的选择器的类。这个类其实就是自动配置的加载选择器。

    @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 .在这个类有一个重要的方法 getCandidateConfigurations.用于加载 Spring Boot 配置的自动配置类。

    getAutoConfigurationEntry 会筛选出有效的自动配置类。

    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);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }   
    
    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;
        }
    

    下图是 DEBUG 模式下筛选之后的结果,因为我只添加了 web 模块,所以只有 web 相关的自动配置。


    筛选过后的自动配置

    3. xxxAutoConfiguration 与 xxxProperties

    在上面的 debug 里,我们看到了成功加载的自动配置,目前只看到了配置类,却还没有发现自动配置值,随便选择一个 AutoConfiguration 查看源码。

    这里选择了 ServletWebServerFactoryAutoConfiguration.

    @Configuration
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    //判断当前项目有没有这个类
    //CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
    @ConditionalOnClass(ServletRequest.class)
    //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
    //满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(ServerProperties.class)
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
            ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
            ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
            ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
    

    需要注意的是 @EnableConfigurationProperties(ServerProperties.class).他的意思是启动指定类的
    ConfigurationProperties功能;将配置文件中对应的值和 ServerProperties 绑定起来;并把
    ServerProperties 加入到 IOC 容器中。

    再来看一下 ServerProperties .

    @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
    public class ServerProperties {
    
        /**
         * Server HTTP port.
         */
        private Integer port;
    

    显而易见了,这里使用 ConfigurationProperties 绑定属性映射文件中的 server 开头的属性。结合默认配置

    # 路径spring-boot-autoconfigure-2.1.1.RELEASE.jar
    # /META-INF/spring-configuration-metadata.json
    
        {
          "name": "server.port",
          "type": "java.lang.Integer",
          "description": "Server HTTP port.",
          "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
          "defaultValue": 8080
        }
    

    达到了自动配置的目的。

    4. 自动配置总结

    1. SpringBoot 启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 。
    2. @EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。
    3. 筛选有效的自动配置类。
    4. 每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能 。

    5. 配置类

    通过自动配置,我们发现已经帮我们省去了大量的配置文件的编写,那么在自定义配置的时候,我们是不是需要编写XML呢?Spring boot 尽管可以使用 SpringApplicationXML 文件进行配置,但是我们通常会使用 @Configuration 类进行代替,这也是官方推荐的方式。

    5.1 XML配置

    定义 helloService Bean.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="helloService" class="net.codingme.boot.service.HelloService"></bean>
    
    </beans>
    

    引入配置。

    @ImportResource(value = "classpath:spring-service.xml")
    @SpringBootApplication
    public class BootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(BootApplication.class, args);
        }
    }
    

    5.2 注解配置

    此种方式和上面的XML配置是等效的,也是官方推荐的方式。@Configuration 注解的类(要在扫描的包路径中)会被扫描到。

    /**
     * <p>
     * 配置类,相当于传统Spring 开发中的 xml-> bean的配置
     *
     * @Author niujinpeng
     * @Date 2018/12/7 0:04
     */
    @Configuration
    public class ServiceConfig {
    
        /**
         * 默认添加到容器中的 ID 为方法名(helloService)
         *
         * @return
         */
        @Bean
        public HelloService helloService() {
            return new HelloService();
        }
    }
    

    6. 附录

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

    文章代码已经上传到 GitHub Spring Boot 自动配置

    <完>
    本文原发于个人博客:https://www.codingme.net 转载请注明出处。

    相关文章

      网友评论

        本文标题:Springboot 系列(三)Spring Boot 自动配置

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