美文网首页我爱编程
SpringBoot原理浅析3-自动配置机制

SpringBoot原理浅析3-自动配置机制

作者: Coding小聪 | 来源:发表于2018-06-10 19:30 被阅读73次

    前言

    在使用Spring或者是Spring MVC时,必须要配置很多的配置项框架才能work,而这些配置项在大部分场景下的取值是相同的。于是SpringBoot提出"约定大于配置",即给这些配置项合理的默认值,省去开发人员的手动配置工作,当某些配置项预定的默认值无法满足应用需求时,再单独重新制定该配置项的值。这就是SpringBoot的自动配置,对使用者来说方便快捷。如果想要修改某项配置,可以在应用工程中的application.properties或application.yml配置文件中重写该配置值。
    如何查看配置文件中都默认支持哪些配置呢?可以去SpringBoot官网查看

    自动配置原理

    要想使用自动配置功能,必须先告诉SpringBoot框架,给我打开自动配置功能,如何实现呢?可以通过@EnableAutoConfiguration注解启用自动配置功能。一般我们会在程序的启动类上使用@SpringBootApplication注解,该注解是个聚合注解,它里面就包含@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 {
    

    @EnableAutoConfiguration的源码如下

    @SuppressWarnings("deprecation")
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        /**
         * Exclude specific auto-configuration classes such that they will never be applied.
         * @return the classes to exclude
         */
        Class<?>[] exclude() default {};
    
        /**
         * Exclude specific auto-configuration class names such that they will never be
         * applied.
         * @return the class names to exclude
         * @since 1.3.0
         */
        String[] excludeName() default {};
    
    }
    

    @EnableAutoConfiguration的主要作用是向Spring容器中注入了EnableAutoConfigurationImportSelector对象,主要通过该对象完成自动配置的功能。

    我们可以通过@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)关闭特定的自动配置。

    Tips:Spring框架当中定义了很多@Enable*注解,除了@EnableAutoConfiguration之外,常见的还有@EnableCaching、@EnableAsync、@EnableAspectJAutoProxy。它们的作用都是在框架中启用某种功能,它们的原理都是通过@Import注解往容器中注入某个Bean对象。

    EnableAutoConfigurationImportSelector类的继承关系如下所示

    ImportSelector(接口,方法:selectImports())
        DeferredImportSelector(接口,无方法)
            AutoConfigurationImportSelector
                EnableAutoConfigurationImportSelector
    

    EnableAutoConfigurationImportSelector类中没有selectImports()方法,其selectImports()方法在AutoConfigurationImportSelector类中实现,代码如下:

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        try {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
            //获取所有在配置文件中定义的xxxAutoConfiguration类的全路径名
            List<String> configurations = getCandidateConfigurations(annotationMetadata,
                    attributes);
            // 去除重复的xxxAutoConfiguration
            configurations = removeDuplicates(configurations);
            // 对xxxAutoConfiguration进行排序
            configurations = sort(configurations, autoConfigurationMetadata);
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return configurations.toArray(new String[configurations.size()]);
        }
        catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }
    

    该方法主要做了以下几件事

    1. 获取所有在配置文件中定义的xxxAutoConfiguration类的全路径名;
    2. 对获取到的AutoConfiguration类名进行去重及排序;
    3. 移除被声明为排除的自动配置类;
    4. 过滤AutoConfiguration,选出当前应用匹配的自动配置类;
    5. 广播AutoConfigurationImportEvent事件。

    注意这里只是加载了自动配置的类名,还没有将其注册到Spring容器当中,只有将这些自动配置类注册到Spring容器当中,它们才会生效。

    获取所有在配置文件中定义的xxxAutoConfiguration类的全路径名

    getCandidateConfigurations()方法会委托给SpringFactoriesLoader.loadFactoryNames()执行,其方法的代码如下

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                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 ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    

    urls表示资源加载的路径,它会循环转换成Properties,然后从Properties中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration。我调试的应用urls的取值如下:

    jar:file:/D:/app/apache-maven-3.3.9/m2/org/springframework/boot/spring-boot/1.5.4.RELEASE/spring-boot-1.5.4.RELEASE.jar!/META-INF/spring.factories
    jar:file:/D:/app/apache-maven-3.3.9/m2/org/springframework/boot/spring-boot-autoconfigure/1.5.4.RELEASE/spring-boot-autoconfigure-1.5.4.RELEASE.jar!/META-INF/spring.factories
    jar:file:/D:/app/apache-maven-3.3.9/m2/org/springframework/spring-beans/4.3.9.RELEASE/spring-beans-4.3.9.RELEASE.jar!/META-INF/spring.factories
    

    spring-boot-autoconfigure-1.5.4.RELEASE.jar!/META-INF/spring.factories文件中定义了如下自动配置类的内容。

    # 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.cassandra.CassandraAutoConfiguration,\
    org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
    org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
    org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
    org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
    org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
    org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
    org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
    org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
    org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
    org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
    org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
    org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
    org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
    org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
    

    截至到selectImports()方法的调用栈如下所示:


    对于自动配置机制来说,我们可以简单理解为:从配置文件中获取自动配置类是发生在AbstractApplicationContext.refresh()方法中的invokeBeanFactoryPostProcessors()调用过程中。

    到目前为止我们还没有向Spring容器注入加载出来的自动配置类,而注入自动配置类发生在AbstractApplicationContext.refresh()方法中的onRefresh()
    我们在HttpEncodingAutoConfiguration类的构造方法打个断点可以看看其方法调用栈。

    常用组件的自动配置分析

    在spring-boot-autoconfigure包中,定义了开发过程中常用组件要完成自动化配置代码模块。


    常用组件的自动配置实现原理都很类似,下面分析一下HttpEncoding自动化配,首先来看看HttpEncodingAutoConfiguration的代码

    @Configuration
    //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
    @EnableConfigurationProperties(HttpEncodingProperties.class) 
    @ConditionalOnWebApplication
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    public class HttpEncodingAutoConfiguration {
    
        private final HttpEncodingProperties properties;
    
        public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
            this.properties = properties;
        }
    
        @Bean
        @ConditionalOnMissingBean(CharacterEncodingFilter.class)
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        ...
    
    }
    

    @Configuration表明HttpEncodingAutoConfiguration是一个配置类,可以在其中定义Bean对象。
    @ConfigurationProperties注解主要用来把properties配置文件转化为bean来使用的,而@EnableConfigurationProperties注解的作用是@ConfigurationProperties注解生效。如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的。

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

    可以发现HttpEncodingAutoConfiguration中使用了很多@Conditional*注解,它们都是从@Conditional扩展而来,表示只有当条件成立时,才会向Spring容器中添加组件,如果多个注解用在配置类上,则表示只有当所有@Conditional*中的条件都被满足时整个配置类才能生效。常见@Conditional*的含义如下表所示

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

    看看HttpEncodingProperties的源码

    @ConfigurationProperties(prefix = "spring.http.encoding")
    public class HttpEncodingProperties {
    
        public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    
        /**
         * Charset of HTTP requests and responses. Added to the "Content-Type" header if not
         * set explicitly.
         */
        private Charset charset = DEFAULT_CHARSET;
    
        /**
         * Force the encoding to the configured charset on HTTP requests and responses.
         */
        private Boolean force;
    
        /**
         * Force the encoding to the configured charset on HTTP requests. Defaults to true
         * when "force" has not been specified.
         */
        private Boolean forceRequest;
    
        /**
         * Force the encoding to the configured charset on HTTP responses.
         */
        private Boolean forceResponse;
    
        /**
         * Locale to Encoding mapping.
         */
        private Map<Locale, Charset> mapping;
    
        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;
        }
    
        boolean shouldForce(Type type) {
            Boolean force = (type == Type.REQUEST ? this.forceRequest : this.forceResponse);
            if (force == null) {
                force = this.force;
            }
            if (force == null) {
                force = (type == Type.REQUEST);
            }
            return force;
        }
    
        enum Type {
    
            REQUEST, RESPONSE
    
        }
    
    }
    

    如果要在配置文件中定义HttpEncoding相关的配置,则可以在配置文件中写入spring.http.encoding.x,其中x表示HttpEncodingProperties中的属性。

    可以总结出自动配置的大体套路是定义下面两个类:

    1. xxxxAutoConfigurartion:自动配置类,给容器中添加需要的组件;
    2. xxxxProperties:封装配置文件中相关属性,所有在配置文件中可以配置的属性都是在xxxxProperties类中封装者,配置文件能配置什么就可以参照某个功能对应的这个属性类。

    自定义自动配置

    请参考:初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置

    相关文章

      网友评论

        本文标题:SpringBoot原理浅析3-自动配置机制

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