1. 程式人生 > >SpringBoot自動配置的原理及實現

SpringBoot自動配置的原理及實現

SpringBoot自動配置的實現原理

SpringBoot的核心就是自動配置,自動配置又是基於條件判斷來配置Bean。關於自動配置的原始碼在spring-boot-autoconfigure-2.0.3.RELEASE.jar

在這裡插入圖片描述

回顧配置屬性

在通常需要我們在property中配置資訊時,通常使用@ConfigurationProperties(pefix=“字首”)註解的方式從配置檔案中獲取配置,如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.context.properties.ConfigurationProperties;

@RestController
@ConfigurationProperties(prefix = "test")
//@Component //如果這裡添加了註解那麼在自動配置類的時候就不用新增@enableConfigurationProperties(HelloProperties.class)註解.
public class Demo {

 
	
    private String msg="default";//現在我們在配置檔案寫hello.msg=world,因為簡單就不再展示;如果那麼預設為default.

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
	}
	
	
	@RequestMapping("/msg")
	public Object index(){
		return this.msg;
	}

}

application.yml中配置資訊

test:
  msg: bamboo

如果把application.yml中的配置資訊註釋掉則預設使用default值,否則使用配置資訊中的值,以上便是普通配置方式

解析

SpringBoot執行原理
先看@SpringBootApplication

@SpringBootConfiguration:標記當前類為配置類
@EnableAutoConfiguration:開啟自動配置
@ComponentScan:掃描主類所在的同級包以及下級包裡的Bean
關鍵是@EnableAutoConfiguration

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

而通過@Import(AutoConfigurationImportSelector.class)匯入的配置功能,
AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的類名集合,這個集合就是所有需要進行自動配置的類,而是是否配置的關鍵在於META-INF/spring.factories檔案中是否存在該配置資訊

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

開啟,如下圖可以看到所有需要配置的類全路徑都在檔案中,每行一個配置,多個類名逗號分隔,而\表示忽略換行
在這裡插入圖片描述

以SpringApplicationAdminJmxAutoConfiguration類來看其主要構成部分

@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class}) //配置完JmxAutoConfiguration後再配置當前型別
// spring.application.admin為字首,屬性為enabled,有值時為true,沒有匹配到則為false:以上條件為true則例項化,否則不是例項化
@ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true",  matchIfMissing = false)
public class SpringApplicationAdminJmxAutoConfiguration 

都能看到各種各樣的條件判斷註解,滿足條件時就載入這個Bean並例項化
此類的條件註解是:@ConditionalOnProperty

@ConditionalOnBean:當容器裡有指定Bean的條件下
@ConditionalOnClass:當類路徑下有指定的類的條件下
@ConditionalOnExpression:基於SpEL表示式為true的時候作為判斷條件才去例項化
@ConditionalOnJava:基於JVM版本作為判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查詢指定的位置
@ConditionalOnMissingBean:當容器裡沒有指定Bean的情況下
@ConditionalOnMissingClass:當容器裡沒有指定類的情況下
@ConditionalOnWebApplication:當前專案時Web專案的條件下
@ConditionalOnNotWebApplication:當前專案不是Web專案的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑是否有指定的值
@ConditionalOnOnSingleCandidate:當指定Bean在容器中只有一個,或者有多個但是指定首選的Bean
這些註解都組合了@Conditional註解,只是使用了不同的條件組合最後為true時才會去例項化需要例項化的類,否則忽略
這種spring4.X帶來的動態組合很容易後期配置,從而避免了硬編碼,使配置資訊更加靈活多變,同時也避免了不必要的意外異常報錯。使用的人只要知道配置的條件即可也不用去閱讀原始碼,方便快捷,這也是sprignboot快捷方式帶來的好處

參考HttpEncodingAutoConfiguration配置資訊如下

@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

@Configuration:標明為配置類
@EnableConfigurationProperties(HttpEncodingProperties.class)宣告開啟屬性注入
@ConditionalOnClass(CharacterEncodingFilter.class)當CharacterEncodingFilter在類路徑的條件下
@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true)當spring.http.encoding=enabled的情況下,如果沒有設定則預設為true,即條件符合
@ConditionalOnMissingBean當容器中沒有這個Bean時新建Bean

案例擴充套件


/**
 * @author wuweifeng wrote on 2017/11/25.
 * 根據部署環境動態決定是否啟用eureka
 線上的環境開啟eureka,就在application-prod.yml裡配上open.eureka=true,
 其他的yml什麼也不寫就行了。這樣本地啟動時就相當於沒有開啟EnableDiscoveryClient
 */
@Component
@ConditionalOnProperty(value = "open.eureka")
@EnableDiscoveryClient
public class JudgeEnableDiscoveryClient 

自己實現一個自己的自動配置

專案

xm-common:普通jar專案
- src/main
    java
        BambooServer.java  需要被例項化的服務類
        BambooServerProperties.java 配置資訊屬性類
        BmbooServiceAutoConfiguration.java 自動配置類
    resources
        META-INF/spring.factories 配置自動配置的屬性檔案
demo:普通springboot-web專案

需要例項化的服務類

public class BambooServer {
    private String name;

    public String sayServerName(){
        return "I'm " + name + "! ";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

配置資訊對應的屬性對映類,需要pom中加入spring-boot-starter依賴

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "bamboo")
public class BambooServerProperties {

    private static final String NAME = "bamboo_server0";

    private String name = NAME;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

自動配置檔案

/**
 * Author: bamboo
 * Time: 2018/11/25/025
 * Describe: 自動配置類
 * 根據條件判斷是否要自動配置,建立Bean
 */
@Configuration
@EnableConfigurationProperties(BambooServerProperties.class)
@ConditionalOnClass(BambooServer.class)//判斷BambooServer這個類在類路徑中是否存在
@ConditionalOnProperty(prefix = "bamboo",value = "enabled",matchIfMissing = true)
public class BmbooServiceAutoConfiguration {

    @Autowired
    private BambooServerProperties mistraServiceProperties;

    @Bean(name = "bambooServer")
    @ConditionalOnMissingBean(BambooServer.class)//當容器中沒有這個Bean時(BambooServer)就自動配置這個Bean,Bean的引數來自於BambooServerProperties
    public BambooServer mistraService(){
        BambooServer mistraService = new BambooServer();
        mistraService.setName(mistraServiceProperties.getName());
        return mistraService;
    }
}

在建立如下路徑檔案src/main/resources/META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bamboo.common.autoconfigure.bamboo.BmbooServiceAutoConfiguration

必須是自動配置類的全路徑

mvn install 該專案

建立一個springboot-mvc專案pom依賴上面的jar

@SpringBootApplication
@RestController
@Import(value = {CorsConfig.class, LogFilter.class}) //跨域,介面訪問請求日誌
public class DemoApplication {
	@Autowired
	private BambooServer bmbooService;

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@RequestMapping("/")
	public Object index(){
		return "helll demo"+bmbooService.getName()+DateUtils.getDate();
	}
}

在applicaton.yml中加,重啟重新整理則會更新為如下資訊

bamboo:
  name: 測試服務