SpringBoot自動(dòng)配置原理

springboot的自動(dòng)配置流程:

主程序啟動(dòng)會(huì)掃描@SpringBootApplication
@SpringBootApplication
public class HelloWorldMainApplication {

    public static void main(String[] args) {

        SpringApplication.run(HelloWorldMainApplication.class,args);
    }
}
@SpringBootApplication元信息

起到主要作用的是@SpringBootConfiguration和@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 {

@SpringBootConfiguration:
其實(shí)就是@Configuration,標(biāo)注當(dāng)前類(lèi)為主配置類(lèi)(主容器),當(dāng)前類(lèi)指的HelloWorldMainApplication

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

@EnableAutoConfiguration:
包含兩個(gè)重要注解,@AutoConfigurationPackage和@Import({EnableAutoConfigurationImportSelector.class})

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@AutoConfigurationPackage的作用:相當(dāng)于包掃描器,掃描主配置類(lèi)所在包下的所有自定義的bean,例如@Controller、@Service等注解標(biāo)識(shí)的bean。
它的內(nèi)部是通過(guò)@Import({Registrar.class}),利用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,向BeanDefinitionRegistry注冊(cè)主配置類(lèi)所在包下自定義的邏輯組件

@Import({EnableAutoConfigurationImportSelector.class})的作用:通過(guò)ImportSelector選擇器執(zhí)行selectImports方法獲得最終啟用(符合當(dāng)前場(chǎng)景)的自動(dòng)配置類(lèi)的全類(lèi)名的String數(shù)組,主配置類(lèi)就會(huì)把這些自動(dòng)配置類(lèi)的bean加載進(jìn)spring容器,@Configuration其實(shí)也是spring容器的組件,內(nèi)部@Component修飾

selectImports方法內(nèi)部:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
       if (!this.isEnabled(annotationMetadata)) {
           return NO_IMPORTS;
       } else {
           try {
               AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
               AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
               List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
               configurations = this.removeDuplicates(configurations);
               configurations = this.sort(configurations, autoConfigurationMetadata);
               Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
               this.checkExcludedClasses(configurations, exclusions);
               configurations.removeAll(exclusions);
               configurations = this.filter(configurations, autoConfigurationMetadata);
               this.fireAutoConfigurationImportEvents(configurations, exclusions);
               return (String[])configurations.toArray(new String[configurations.size()]);
           } catch (IOException var6) {
               throw new IllegalStateException(var6);
           }
       }
   } 

自動(dòng)配置的核心就在于selectImports方法,下面一步步的剖析這個(gè)方法

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
    }
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
        try {
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
            Properties properties = new Properties();

            while(urls.hasMoreElements()) {
                properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)urls.nextElement())));
            }

            return loadMetadata(properties);
        } catch (IOException var4) {
            throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", var4);
        }
    }

loadMetadata方法內(nèi)部遍歷應(yīng)用下的所有jar,加載了spring-boot-autoconfigure-1.5.9.RELEASE.jar下的META-INF/spring-autoconfigure-metadata.properties,配置文件里面的信息為自動(dòng)配置類(lèi)xxxAutoConfiguration的@ConditionalOnClass、@AutoConfigureAfter、@AutoConfigureOrder等注解參數(shù)的key-value信息,因?yàn)樽詣?dòng)配置類(lèi)是否啟用要看符不符合這些條件,如果啟用,還要對(duì)這些自動(dòng)配置類(lèi)的加載順序進(jìn)行排序,返回的autoConfigurationMetadata 封裝了這些信息的Properties,Properties把spring-autoconfigure-metadata.properties封裝了

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

參數(shù)annotationMetadata為主啟動(dòng)類(lèi)的信息(以及它的注解及元注解信息),返回的attributes為@EnableAutoConfiguration注解的exclude和excludeName的值

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
            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);
        }
    }

只截取一部分,org.springframework.boot.autoconfigure.EnableAutoConfiguration為key

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,\

getCandidateConfigurations方法返回所有自動(dòng)配置類(lèi)的全類(lèi)名集合(從META-INF/spring.factories原封不動(dòng)地拿),參數(shù)getSpringFactoriesLoaderFactoryClass()其實(shí)就EnableAutoConfiguration.class類(lèi)對(duì)象,loadFactoryNames方法內(nèi)部會(huì)讀取該應(yīng)用所有jar包下的META-INF/spring.factories,(其實(shí)只有spring-boot-autoconfigure-1.5.9.RELEASE.jar包下有)取出factories文件中key為EnableAutoConfiguration全類(lèi)名的值的集合,裝進(jìn)集合configurations 返回

configurations = this.removeDuplicates(configurations);

返回原集合,不必理會(huì)

configurations = this.sort(configurations, autoConfigurationMetadata);

對(duì)集合排序,根據(jù)autoConfigurationMetadata里面的AutoConfigureOrder、AutoConfigureAfter、AutoConfigureBefore等信息對(duì)自動(dòng)配置類(lèi)集合configurations排序,自動(dòng)配置類(lèi)加載是有順序的,
例如WebMvcAutoConfiguration,就必須等到DispatcherServletAutoConfiguration加載成功之后再加載,而且還要根據(jù)@Order的派生注解@AutoConfigureOrder決定加載優(yōu)先級(jí)

@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);

處理需要排除的自動(dòng)配置類(lèi),不必理會(huì)

configurations = this.filter(configurations, autoConfigurationMetadata);

根據(jù)@ConditionalOnClass(類(lèi).Class)、@ConditionalOnMissingBean(類(lèi).Class)等注解,判斷注解中的類(lèi).Class是否存在或不存在,從而篩選出符合啟用的自動(dòng)配置類(lèi)。
例如我們的應(yīng)用沒(méi)有導(dǎo)入redis場(chǎng)景的starter,而RedisAutoConfiguration里

@Configuration
@ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
@EnableConfigurationProperties({RedisProperties.class})
public class RedisAutoConfiguration {

如果我們應(yīng)用里沒(méi)有這三個(gè)JedisConnection.class, RedisOperations.class, Jedis.class,那么RedisAutoConfiguration就不會(huì)生效,filter方法會(huì)把它過(guò)濾掉,最終返回的configurations就是經(jīng)過(guò)過(guò)濾后的、符合我們當(dāng)前應(yīng)用場(chǎng)景的自動(dòng)配置類(lèi)的String數(shù)組。

這些自動(dòng)配置類(lèi)在被@Import進(jìn)主容器之后,spring創(chuàng)建這些自動(dòng)配置類(lèi)的bean,這些bean各自完成初始化自己內(nèi)部組件的工作
至此,springboot自動(dòng)配置類(lèi)的默認(rèn)初始化完成

springboot修改默認(rèn)配置:

springboot為我們自動(dòng)配置了各個(gè)場(chǎng)景下的所有默認(rèn)配置,那它是如何設(shè)置的默認(rèn)配置呢,再者如果我們需要修改默認(rèn)配置,應(yīng)該從哪入手?

首先要明確一點(diǎn),每個(gè)自動(dòng)配置類(lèi)的主要功能就是給容器中加入所需的組件

以HttpEncodingAutoConfiguration為例

例如HttpEncodingAutoConfiguration類(lèi)給容器中加入一個(gè)解決編碼的CharacterEncodingFilter組件
HttpEncodingAutoConfiguration類(lèi)信息如下:

@Configuration //表示這是一個(gè)配置類(lèi),以前編寫(xiě)的配置文件一樣,也可以給容器中添加組件
@EnableConfigurationProperties(HttpEncodingProperties.class) //啟動(dòng)指定類(lèi)的 ConfigurationProperties功能;將配置文件中對(duì)應(yīng)的值和HttpEncodingProperties綁定起來(lái);并把 HttpEncodingProperties加入到ioc容器中,以便自動(dòng)配置類(lèi)以有參構(gòu)造器的方式注入Properties
@ConditionalOnWebApplication //Spring底層@Conditional注解(Spring注解版),根據(jù)不同的條件,如果 滿(mǎn)足指定的條件,整個(gè)配置類(lèi)里面的配置就會(huì)生效; 判斷當(dāng)前應(yīng)用是否是web應(yīng)用,如果是,當(dāng)前配置類(lèi)生效
@ConditionalOnClass(CharacterEncodingFilter.class) //判斷當(dāng)前項(xiàng)目有沒(méi)有這個(gè)類(lèi),自動(dòng)配置類(lèi)是否啟動(dòng)要看這個(gè)注解; CharacterEncodingFilter;SpringMVC中進(jìn)行亂碼解決的過(guò)濾器;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //判斷配置文件中是否存在某個(gè)配置 spring.http.encoding.enabled;如果不存在,判斷也是成立的 //即使我們配置文件中不配置pring.http.encoding.enabled=true,也是默認(rèn)生效的;
public class HttpEncodingAutoConfiguration {

private final HttpEncodingProperties properties;

public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
    this.properties = properties;
    }
@Bean //給容器中添加一個(gè)組件,這個(gè)組件的某些屬性值需要從properties中獲取
@ConditionalOnMissingBean(CharacterEncodingFilter.class) //判斷容器有沒(méi)有這個(gè)組件? 沒(méi)有才會(huì)生效
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;
    }
}

我們只需要指定HttpEncodingProperties類(lèi)中的charset屬性的值即可,而HttpEncodingProperties通過(guò)@ConfigurationProperties(prefix = "")又和配置文件綁定了,
這里說(shuō)的配置文件是我們應(yīng)用的配置文件,因?yàn)閟pring-boot-autoconfigure-1.5.9.RELEASE.jar沒(méi)有配置文件,會(huì)掃描我們應(yīng)用的配置文件,
只要對(duì)應(yīng)上prefix前綴和HttpEncodingProperties類(lèi)的屬性名就行了

HttpEncodingProperties類(lèi)如下:
可以看出charset的默認(rèn)值是UTF-8

@ConfigurationProperties(prefix = "spring.http.encoding") //從配置文件(我們自己應(yīng)用的配置文件)中獲取指定的值和bean的屬性進(jìn)行綁定
public class HttpEncodingProperties {

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private Charset charset;
    private Boolean force;
    private Boolean forceRequest;
    private Boolean forceResponse;
    private Map<Locale, Charset> mapping;
    public HttpEncodingProperties() {
            this.charset = DEFAULT_CHARSET;
        }
    set/get方法......
}

我們自己在application.properties中自定義配置

spring.http.encoding.enabled=true
spring.http.encoding.charset=gbk
spring.http.encoding.force=true
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容