1. 程式人生 > >SpringBoot 配置及原理

SpringBoot 配置及原理

一、Spring Boot全域性配置檔案:1)、application.properties名稱固定。2)、application.yml 配置檔案。兩個檔案的作用:可以覆蓋SpringBoot配置的預設值。
   ◀ YAML(is not a Markup Language:不僅僅是一個標記語言):以前的配置檔案,大多是xx.xml檔案,而YAML是以資料為中心,比json、xml等更適合做配置檔案。

#普通配置檔案.properties的語法
#server.port=80

#XML的寫法
#<server>
#   <port>8080<port/>
#<server/>

#yml 以資料為中心的語法
server:
  port: 8080

   ◀ YML語法:基本語法:k:(空格)v--->表示一對鍵值對。(以空格縮排來控制層級關係;只要是左對齊的一列資料,都是同一層級)屬性和值也是大小寫敏感。

server:
  port: 8080
  path: /hello
spring:
  profiles: dev

   ◀ 值得寫法:1)字面量:普通的值(數字、字串、布林),字串預設是不用加上單引號或者雙引號,但也可以加,但是有區別:雙引號,不會轉義字串裡面的特殊字元,特殊字元會作為本身想表達的意思。單引號,會轉義特殊字元,特殊字元最終只是一個普通的字串資料。
    2)、物件(屬性和值)或者Map(鍵值對)的表達—>k: v形式,物件還是k: v的方式。比較抽象,我們舉個栗子看看:

#yml正常寫法
friends:
    lastName: zhangsan           
    age: 20

#行內寫法
friends: {lastName: zhangsan,age: 20}

   3)、陣列(List、Set)用 ‘- 值’ 表示陣列中的一個元素,也舉一個栗子:

# yml正常寫法 -值 形式
pets:
   - cat
   - dog
#行內寫法
pets: [cat,dog]

   ◀ 配置檔案注入:測試上面資料賦值是否正確。
   1)、準備配置檔案:application.yml

person:
  lastName: zhangsan
  age: 20
  boss: false
  birth: 2018/8/20
  map: {k1: v1,k2: v2}
  lists: [listi,zhaoliu]
  dog:
    name: gou
    age: 2

   2)、準備JavaBean@ConfigurationProperties(prefix ="person")表示將配置檔案中的person的每一個屬性對映到這個元件中,但只有這個元件是容器中的元件,才能提供功能。需要使用@Component標註才能成為容器元件。

@Component
@ConfigurationProperties(prefix ="person")
public class Person {
    private String lastName;
    private Integer age;
    private boolean boss;
    private Date birth;
    private Map<String,String> map;
    private List lists;
    private Dog dog;
}

   3)、優化:當準備2中的檔案,會提示我們“Spring Boot Configuration Annotation ...”點選去後會發現如下starters資訊,那麼我們將此配置於pom檔案中,作用:當我們在配置檔案中,給帶有@ConfigurationProperties的實體類賦值時會有屬性提示。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

   4)、測試: 進入test目錄底下的類目錄,直接匯入person輸入,檢視是否已賦值即可。

//使用Spring的驅動器,不用再使用JUnit的驅動器了
@RunWith(SpringRunner.class)
@SpringBootTest
public class HellowordQuickStartApplicationTests {
        //在測試期間可以類似編碼一樣進行自動注入
	@Autowired
	Person person;

        @Test
	public void testPersion(){
		System.out.println(person);
	}
}

   5)、properties中的語句與yml不同,以下是properties的配置語句。

#person.lastName=張三 也是可以的
person.last-name=張三
person.age=18
person.birth=2013/04/23
person.boss=false
person.map.key1=v1
person.map.key2=v2
person.lists=a,b,c
person.dog.name=dog
person.dog.age=2

6)、測試的時候可能會出現亂碼,設定如下。properties預設的編碼時ASK碼,我們需要將其設定為UTF-8來解決亂碼問題。

 7)、第二種賦值方式:@ Value(“字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}”)---三種傳值方式

@Component
//@ConfigurationProperties(prefix ="person")
public class Person {
    //@email @Value不支援校驗(JSR303資料校驗)
    @Value("${person.last-name}")
    private String lastName;
    //SPEL
    @Value("#{22*3}")
    private Integer age;
    @Value("true")
    private boolean boss;

 ● @Vaule@ConfigurationProperties兩者的區別如下:(其實@Value最多用在獲取單個值的時候使用)

  @configuration @value
功能 批量注入配置檔案中的屬性 每個屬性單獨配置
鬆散繫結(鬆散語法) 支援(大小寫不敏感) 不支援(與配置檔案保持一致)
SpEL 不支援(不能用於邏輯計算) 支援#{邏輯計算}
JSR303資料校驗 支援@validated 不支援
複雜型別封裝 支援 不支援(map物件)

二、@PropertySource@ConfigurationProperties之間的區別:@ConfigurationProperties預設從全域性配置檔案中載入值。
  ● @PropertySource:指向自己定義的properties配置檔案,新建person.properties配置檔案(省略),如下獲取值。

//優先順序高於@ConfigurationProperties(prefix ="person")
@PropertySource(value = {"classpath:person.properties"})
@Component
//@ConfigurationProperties(prefix ="person")
//@Validated
public class Person {

  ● @ImportResource:匯入Spring的配置檔案,讓配置檔案裡面的內容生效 。
    1)、定義配置檔案bean.xml(以前的配置,SpringBoot不這麼用)

<?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="com.atguigu.Servers.HelloService"></bean>
</beans>

    2)、在主程式中使用@ImportResource註解匯入bean.xml配置檔案

@ImportResource(locations={"classpath:bean.xml"})
@SpringBootApplication
public class HellowordQuickStartApplication {

  ● SpringBoot中(配置類====配置檔案xml)推薦使用配置類,如下建立:

//@Configuration指明當前類是一個配置類 替代配置檔案
@Configuration
public class MyAppConfig {
    //@bean註解就相當於<bean></bean>標籤
    @Bean
    //方法名就相當於xml中的id , 專案啟動時就會將元件加入容器中
    public HelloService helloService(){
        System.out.println("@Bean給容器中新增元件");
        return new HelloService();
    }
}

三、配置檔案佔位符
 1)、隨機數(瞭解即期)

${random.value}、${random.int}、${random.log}、${random.int(10)}

 2)、佔位符(當屬性不存在時,可以給一個預設值,例如下面age屬性值得獲取)

persion.last-name=張三
persion.dog.name=${persion.last_name}_dog
perdion.dog.age=${persion.noexistage:20}

四、Profile:是Spring對不同環境提供不同配置功能的支援,可以通過啟用、指定引數等方式快速切換環境:
  1)、多profile檔案形式(瞭解一下,我們使用更多的是2中的yml形式):
    我們可以編寫多個配置檔案,對應多個場景(開發、測試、生產等),檔名可以是application-{profile}.properties/yml的形式命名,例如:application-dev.properties
   
    專案啟動時預設使用application.properties的配置,如果要啟用開發配置檔案,在application.properties中輸入如下啟用資訊。

#啟用dev開發模式的配置檔案,就不用application.properties檔案的配置了
spring.profiles.active=dev

    2)、yml支援多文件塊方式(推薦使用):通過“---”來劃分文件塊,Document表示所處模組的位置/總塊 。

# --- 稱為多文件快 , 簡寫1中的形式
---
server:
  port: 8080
  path: /hello
# 如下為啟用profiles ,如果不啟用則預設為Document1中的配置
spring:
  profiles:
    active: dev
---
server:
  port: 8084
spring:
  profiles: prod
---
server:
  port: 8081
spring:
  profiles:
    active: dev

    3)、啟用指定profile方式,上面用的都是第一種:
    (1)、在預設配置application.properties中設定spring.profiles.active屬性 

        spring.profiles.active=dev

    (2)、命令列:--spring.profiles.active=dev

命令列執行jar包的方式:java -jar xxx.jar --spring.profiles.active=dev;
    (3)、虛擬機器引數:-Dspring.profiles.active=prod;

五、配置檔案載入位置:也就優先順序
       SpringBoot啟動會掃描一下位置的application.properties或者application.yml檔案作為SpringBoot的預設配置檔案
            (1)、file:./config/  (專案底下的config目錄)
            (2)、file:./   (直接位於專案底下的配置檔案)
            (3)、classpath:/config/ (config檔案預設沒生成,需要自己建立)
            (4)、classpath:/  (專案建立後,配置檔案預設位置)
    以上是按照優先順序從高到低的順序,所有位置的檔案都會被載入,高優先順序配置內容會覆蓋低優先順序配置的相同內容
    我們也可以通過spring.config.location來改變預設配置(專案打包成功以後,我們可以使用命令列引數的形式,啟動專案來指定配置檔案的新位置;指定的配置檔案會共同起作用,形成互補作用),這個優先順序肯定最高了。而且我們要知道,打jar包的時候只包含src底下的main和resource檔案,1、2中的不會被打包進去。其實將jar包與配置檔案*.yml等放在同一個目錄下的情況也比較多常見,因為靈活。專案啟動時可以自動載入同目錄下的*.yml等配置檔案。且優先順序高於內部的配置檔案,之間互補配置。

#命令列新增配置,優先順序最高
java -jar xxx.jar --spring.config.location=d:\xxx.properties

六、自動配置原理:
【1】、SpringBoot啟動的時候載入主配置類,@SpringBootApplication下開啟了主配置功能@EnableAutoConfiguration
【2】、@EnableAutoConfiguration作用:1)、利用EnableAutoConfigurationImportSelector給容器匯入一些元件。
             2)、可以檢視selectImports()方法的內容:
                List configurations = getCandidateConfigurations(annotationMetadata, attributes);獲取候選的配置。
                SpringFactoriesLoader.loadFactoryNames()掃描所有jar包類路徑下 META‐INF/spring.factories把掃描到的這些檔案的內容包裝成properties物件從properties中獲取到EnableAutoConfiguration.class類(類名)對應的值,然後把他們新增在容器中
【3】、將 類路徑下 META-INF/spring.factories 裡面配置的所有EnableAutoConfiguration的值加入到了容器中;

# EnableAutoConfiguration  對應 @EnableAutoConfiguration
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.CassandraReactiveDataAutoConfiguration,\

每一個這樣的 xxxAutoConfiguration類都是容器中的一個元件,都加入到容器中;用他們來做自動配置;
【4】、每一個自動配置類進行自動配置功能,以HttpEncodingAutoConfiguration(Http編碼自動配置)為例解釋自動配置原理;

@Configuration //表示這是一個配置類,以前編寫的配置檔案一樣,也可以給容器中新增元件
@EnableConfigurationProperties(HttpEncodingProperties.class) //啟動指定類的
ConfigurationProperties功能;將配置檔案中對應的值和HttpEncodingProperties繫結起來;並把
HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication //Spring底層@Conditional註解(Spring註解版),根據不同的條件,如果
//滿足指定的條件,整個配置類裡面的配置就會生效; 判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnClass(CharacterEncodingFilter.class) //判斷當前專案有沒有這個類
CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing =
true) //判斷配置檔案中是否存在某個配置 spring.http.encoding.enabled;如果不存在,判斷也是成立的
//即使我們配置檔案中不配置pring.http.encoding.enabled=true,也是預設生效的;
public class HttpEncodingAutoConfiguration {
    //他已經和SpringBoot的配置檔案映射了
    private final HttpEncodingProperties properties;
    //只有一個有參構造器的情況下,引數的值就會從容器中拿
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = properties;
    }

    @Bean //給容器中新增一個元件,這個元件的某些值需要從properties中獲取
    @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;
    }

根據當前不同的條件判斷,決定這個配置類是否生效?一但這個配置類生效;這個配置類就會給容器中新增各種元件;這些元件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的;

【5】、所有在配置檔案中能配置的屬性都是在xxxxProperties類中封裝者;配置檔案能配置什麼就可以參照某個功能對應的這個屬性類

@ConfigurationProperties(prefix = "spring.http.encoding") //從配置檔案中獲取指定的值和bean的屬
性進行繫結
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");

精髓:1)、SpringBoot啟動會載入大量的自動配置類
         2)、我們看我們需要的功能有沒有SpringBoot預設寫好的自動配置類;
         3)、我們再來看這個自動配置類中到底配置了哪些元件;(只要我們要用的元件有,我們就不需要再來配置了)
         4)、給容器中自動配置類新增元件的時候,會從properties類中獲取某些屬性。我們就可以在配置檔案中指定這些屬性的值;
   xxxxAutoConfigurartion:自動配置類,給容器中新增元件。
   xxxxProperties:封裝配置檔案中相關屬性;

七、@ConditionalOnxxx中的@Conditional派生註解(Spring註解版原生的@Conditional作用)作用:必須是@Conditional指定的條件成立,才給容器中新增元件,配置配裡面的所有內容才生效。

● 自動配置類必須在一定的條件下才能生效,那麼我們如何知道哪些配置類生效哪些沒有生效,其實我們可以通過在配置檔案啟用 debug=true屬性,就可以檢視哪些配置類生效。

debug=true

▶ 通過控制檯列印自動配置報告,我們就可以知道哪些自動配置類生效(Positive matches:匹配成功的自動配置類)

Positive matches:
-----------------

   CodecsAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

   CodecsAutoConfiguration.JacksonCodecConfiguration matched:
      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

   CodecsAutoConfiguration.JacksonCodecConfiguration#jacksonCodecCustomizer matched:
      - @ConditionalOnBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) found bean 'jacksonObjectMapper' (OnBeanCondition)

   DispatcherServletAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - found ConfigurableWebEnvironment (OnWebApplicationCondition)

▶ 自動配置未生效類(Negative matches:匹配失敗的自動配置類)

Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice', 'org.aspectj.weaver.AnnotatedElement' (OnClassCondition)

   ArtemisAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory' (OnClassCondition)