【Spring Boot】3.Spring Boot的配置

簡介

springboot使用一個全局配置文件,配置文件名是固定的:html

  • application.properties
  • application.yml

配置文件的做用:修改springboot自動配置的默認值。springboot在底層都給咱們自動配置好了;java

配置文件位於src/resources下面。web

YAML

yaml a markup language 是一個標記語言,以數據爲中心,比其餘類型的標記語言更適合做爲配置文件。 標記語言:之前的配置文件大多都使用XXX.xml文件,spring

不一樣文件配置方式

YAML:shell

server:
  port: 8081

XML:json

<server>
  <port>8081</port>
</server>

properties:tomcat

server.port = 8081

例子:配置文件實現值的注入

編寫配置文件

person:
  lastName: zhaoyi
  list:
    - list1
    - list2
  maps:
    k1: v1
    k2: v2
  age: 25
  boss: false
  dog:
    name: xiaohuang
    age: 30

配置組件

// person類
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private List<Object> list;
    private Map<String, Object> maps;
    private Integer age;
    private Boolean boss;
    private Dog dog;

    @Override
    public String toString() {
        return "Person{" +
                "lastName='" + lastName + '\'' +
                ", list=" + list +
                ", maps=" + maps +
                ", age=" + age +
                ", boss=" + boss +
                ", dog=" + dog +
                '}';
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public List<Object> getList() {
        return list;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    public Map<String, Object> getMaps() {
        return maps;
    }

    public void setMaps(Map<String, Object> maps) {
        this.maps = maps;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Boolean getBoss() {
        return boss;
    }

    public void setBoss(Boolean boss) {
        this.boss = boss;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }
}

// dog類
package com.zhaoyi.hello1.com.zhaoyi.hello1.bean;

public class Dog {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

使配置擁有提示

導入配置文件處理器,這樣就會有提示編寫資源的信息;springboot

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

只有組件是容器中的組件,才能使用ConfigurationProperties提供的功能;app

編寫測試類

springboot單元測試:能夠在測試期間很方便的相似編碼同樣進行自動注入等容器功能;dom

@RunWith(SpringRunner.class)
@SpringBootTest
public class Hello1ApplicationTests {

    @Autowired
    Person person;

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

properties配置文件的編碼問題

使用properties配置文件

註釋掉application.yml的配置文件信息,新建一個application.properties文件,寫入和applicatio.yml相同功能的配置

person.age=14
person.last-name=張三
person.boss=false
person.maps.k1=v1
person.maps.k2=v2
person.list=a,b,c
person.dog.name=小狗
person.dog.age=5

運行測試後發現有關中文部分的輸出是亂碼。

解決方法

idea使用的是utf-8編碼,在setting處查詢file encoding,設置爲utf-8,並選擇在運行時轉化爲ascll(勾選)

@configurationProperties和@Value

Spring中配置一個Bean:

<bean class="person">
  <property name="lastName" value="zhangsan"></property>
</bean>

其中,value能夠:

  • 字面量
  • ${key} 從環境變量或者配置文件中提取變量值
  • #{SPEL}

而@Value其實效果和其同樣,好比,在類屬性上寫

@Value("person.lastName")
private String lastName
@Value("#{11*2}")
private Integer age

區別

@ConfigurationProperties @Value
功能 批量注入配置文件中的屬性 一個個指定
支持鬆散綁定 不支持鬆散綁定
SpEL 不支持 支持
JSR303數據校驗 支持 不支持
複雜類型封裝 支持 不支持
注:
  • 鬆散綁定例如: 配置文件填寫的是last-name,而@Value必須寫如出一轍的,不然會報錯。而使用@ConfigurationProperties則可使用駝峯式(其實就是類的屬性名)
  • @Validation 在類上加入此配置以後,開啓jsr303校驗,例如在某字段上加上@Email,則配置文件對應字段必須符合郵箱格式。相反,若是此處咱們使用@Value注入值,能夠看到,能夠正常的注入,即使提供的不符合郵箱格式,也不會報錯。

選擇

從上面的說明咱們能夠知道,二者在配置上基本能夠相互替換,彼此功能也大致一致,那麼,咱們在業務場景中,該選用哪種進行編碼呢?

  1. 若是說,咱們只是在某個業務邏輯中須要獲取一下配置文件的某項值,考慮使用@Value.
@RestController
public class HelloController {
    @Value("${person.last-name}")
    private String name;

    @RequestMapping("/")
    public String hello(){
        return "hello world, " + name;
    }
}
  1. 可是,若是咱們專門去寫一個java bean來和配置文件進行映射,那麼毫無疑問,咱們應該使用@ConfigurationProperties

@PopertySource註解

@PropertySource 加載指定的配置文件。

咱們知道,@ConfigurationProperties默認從全局配置文件(application.properties)中獲取值,但一般咱們會將相關的配置文件放到某個配置文件中,例如,咱們將有關person的配置信息放到person.properties中去。爲了使組件類Person可以找到配置文件的配置信息,須要使用增長新的註解@PropertySource指定從哪裏加載配置等相關信息。

@PropertySource("classpath:person.properties")
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
}

@ImportResource註解

@ImportResource 導入Spring的配置文件,讓配置文件裏面的內容生效。

  1. 添加新的服務類HelloService
package com.zhaoyi.hello1.service;
public class HelloService {
}
  1. 在類目錄下建立一個bean.xml,即spring的配置文件,其中配置了一個service的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="com.zhaoyi.hello1.service.HelloService"></bean>
</beans>

那麼問題來了,在spring-boot中顯然是不會加載此bean的,咱們測試一下。

  1. 編寫測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class Hello1ApplicationTests {
    @Autowired
    ApplicationContext ioc;

    @Test
    public void testBean(){
        System.out.println("Is have helloService bean? " + ioc.containsBean("helloService"));
    }
}

測試結果:

Is have helloService bean? false

Spring boot裏面是沒有Spring的配置文件,咱們本身編寫的配置文件,也不能自動識別,想讓Spring的配置文件生效,則須要手動指示將其加載進行,使用@ImportResource標註在一個配置類(例如應用程序啓動類,他也是一個配置類)上:

@ImportResource(locations = {"classpath:bean.xml"})
@SpringBootApplication
public class Hello1Application {
    public static void main(String[] args) {
        SpringApplication.run(Hello1Application.class, args);
    }
}

這時候運行測試用例就會發現,bean已經出如今容器中了。

@Bean註解

@ImportResource(locations = {"classpath:bean.xml"})

通常咱們不會使用6中所提到的這種方式,由於xml配置方式實在是寫了太多的無用代碼,若是xml的標籤聲明,以及頭部的域名空間致使。所以,SpringBoot推薦給容器中添加組件的方式:全註解方式。也就是用配置類來充當bean配置文件。以下,即爲一個配置類:

/**
 * @Configuration 指明當前類是一個配置類
 */
@Configuration
public class MyConfig {
    // 將方法的返回值添加到容器中:容器中這個組件的id就是方法名
    @Bean
    public HelloService helloService(){
        return new HelloService();
    }
}

經過該配置類,能夠爲容器中添加了一個名爲helloService的bean。

配置文件佔位符

不管是使用yaml仍是properties都自持文件佔位符配置方式

隨機數

${random.value}

${random.int}

${random.long}

${random.int(10)}

${random.int[1024,65536]}

屬性佔位符

  • 能夠在配置文件中引用前面配置過的屬性;
  • ${app.name:默認值} 若找不到屬性時,則取默認值處填寫的值;

例子

  1. 在person.properties中寫入以下配置
person.age=14
person.last-name=張三${random.uuid}
person.boss=false
person.maps.k1={person.xxx:novalue}
person.maps.k2=v2
person.list=a,b,c
person.dog.name=${person.last-name}_小狗狗
person.dog.age=${random.int}
  1. 測試輸出
Person{lastName='張三1b9fbbb1-6c58-4a35-8165-ad23800d7456', list=[a, b, c], maps={k2=v2, k1=novalue}, age=14, boss=false, dog=Dog{name='張三b3a355d7-54ce-4afd-9ae6-d3be4aeb4165_小狗狗', age=-122850975}}

注意:留意默認值那一項設置,咱們如願的成功設置了默認值novalue。在實際項目中,這種狀況比較經常使用,稍微留意一下。若是沒有默認值,則會將${xxx}這一段做爲值,這顯然是錯誤的。

9 Profile

profile通常是spring用來作多環境支持的,能夠經過激活指定參數等方式快速的切換當前環境。

9.1 多Profile文件

咱們在主配置文件編寫的時候,文件名能夠是 application-{profile}.properties(yml) 例如: application-dev.properties、application-prod.properties等。

激活指定profile

  1. 在application.properties中指定:
spring.profiles.active=dev
  1. 命令行方式激活,此配置的優先級高於配置文件處的配置。
--spring.profiles.active=dev
  • 方式一 點擊編譯環境右上角的下拉框,選擇第一項edit configuration,在environment配置節中的Program arguments寫入spring.profiles.active=dev便可。
  • 方式二 將當前的項目打包生成jar包,而後執行java -jar your-jar-name.jar --spring.profiles.active=dev便可。
  1. 虛擬機參數方式激活 在步驟2的老地方,Program arguments上一項即爲虛擬機參數激活,不過填寫的內容爲-Dspring.profiles.active=dev,即多了一個-D而已。

yml多文檔配置文件

若咱們使用properties,則須要編寫多個不一樣的配置文件,但若是咱們使用yml的話,則能夠經過多文檔配置節實現單文件管理。注:有關yml相關的知識參考此文檔的前半部分

能夠看到,咱們經過---三個橫線實現文檔分割,同時在每個文檔塊處指定了各個profile的不一樣配置信息,即dev環境下啓動服務使用8082端口,prod環境下使用8083端口。而指定哪個profile則是經過默認的文檔塊(即第一塊)中的spring.profiles.active進行配置。完成如上配置以後咱們啓動服務,顯然此時是以激活的prod環境所配置的端口8083運行的,以下啓動日誌所示:

com.zhaoyi.hello1.Hello1Application      : The following profiles are active: prod
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8083 (http)

也就說,當咱們選定一個profile後,對應的文檔塊的配置就會所有生效。

9 配置文件的加載位置

SpringBoot啓動會掃描如下位置的application.properties或者application.yml文件做爲SpringBoot的默認配置文件:

  • file:./config/
  • file:./
  • classpath:/config/
  • classpath:/

以上是按照優先級順序從高到低,全部位置的文件都會被加載,但對於相同配置:高優先級配置內容會覆蓋低優先級配置的內容,其餘的則互補。

咱們也能夠經過spring.config.location參數來設置默認的配置文件位置。項目打包好之後,使用命令行參數形式來指定配置文件的新位置,指定的配置文件和默認加載的配置互補起做用。而且咱們指定的該配置文件優先級是最高的。

外部配置的加載順序

SpringBoot支持多種外部配置方式,他能夠從如下位置加載配置,按優先級從高到低排列以下(高優先級配置覆蓋低優先級配置,全部配置會造成互補配置):

  1. 命令行參數
java -jar package_name_version.jar --server-port=8080 --server.context-path=/hello

多個參數之間用空格分開,用--parameter_name=value進行配置。

  1. 來自java:comp/env的JNDI屬性
  2. java系統屬性(System.getProperties())
  3. 操做系統環境變量
  4. RandomValuePropertySource配置的random.*屬性值

都是由jar包外向jar包內進行尋找,高優先級的配置覆蓋低優先級的配置。而後 優先加載帶profile的:

  1. jar包外部的application-{profile}.properties或application.yml(帶spring.profile)配置文件

在jar文件的同級目錄放一個application.properties文件,其配置內容會被加載;

  1. jar包內部的application-{profile}.properties或application.yml(帶spring.profile)配置文件

再來加載不帶profile的:

  1. jar包外部的application.properties或application.yml(不帶spring.profile)配置文件
  2. jar包內部的application.properties或application.yml(不帶spring.profile)配置文件
  3. @Configuration註解類上的@PropertySource
  4. 經過SpringApplication.setDefaultProperties指定的默認屬性。

官方文檔列出了比這裏更多的配置文檔,請參考,版本更迭地址會常常變更,可自行前往官方網站進行查看。

自動配置原理

配置文件的配置屬性能夠參照官方文檔:前往

自動配置原理

  1. springboot啓動的時候加載主配置類,開起了自動配置功能@EnableAutoConfiguration
  2. @EnableAutoConfiguration做用:利用EnableAutoConfigurationImportSelector給容器中導入一些組件,能夠查看selectImport()方法的內容:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

掃描全部jar包類路徑下META-INF/spring.factories文件,吧掃描到的這些文件的內容包裝成properties對象;從properties中獲取到EnableAutoConfiguration.class類對應的值,而後把他們添加在容器中;

說到底,就是將類路徑下META-INF/spring.factories裏面配置的全部EnableAutoConfiguration的值加入到了容器中。

# 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.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
...(more)

每個這樣的xxxAutoConfiguration類都會是容器中的一個組件,都加入到了容器中,用他們來作自動配置。

  1. 每個自動配置類進行自動配置功能。 以org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration爲例:
@Configuration
@EnableConfigurationProperties({HttpProperties.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
   ...
}

咱們能夠看到該類的註解以下:

  • @Configuration 毫無疑問,代表這是一個配置類,和咱們要自定義一個配置文件同樣,實現給容器中添加組件;
  • @EnableConfigurationProperties 啓動指定類的ConfigurationPropertiesg功能,這樣就能夠將配置文件中對應的值和HttpEncodingProperties綁定起來;並把HttpEncodingProperties加入到ioc容器中;
  • @ConditionalOnWebApplication Spring底層@Conditional註解,根據不一樣的條件,若是知足指定的條件,整個配置類裏面的配置就會生效;此處判斷的就是當前應用是否是Web應用;不然配置不生效;
  • @ConditionalOnClass 判斷當前項目有沒有類(CharacterEncodingFilter),這是SpringMVC中進行亂碼解決的過濾器;
  • @ConditionalOnProperty 判斷配置文件中是否存在某個配置 spring.http.encoding.enabled;若是不存在,則判斷成立,若是沒有配置,則此處將其設置爲true
  • @Bean 給容器中添加Bean組件,該組件的某些值須要從properties中獲取,此處即爲HttpEncodingProperies,顯然此刻他的取值已經和springboot的properties文件進行注入了;

根據不一樣的條件進行判斷當前這個配置類是否生效。一單這個配置類生效,這個配置類就會給容器中添加各類組件;這些組件的屬性均來自於其對應的Properties類的,這些Properties類裏面的每個屬性,又是和配置文件綁定的。

  1. 全部在配置文件中能配置的屬性都是在xxxProperties類中的封裝者;配置文件能配置什麼就能夠查看對應的屬性類。如上面的這個配置類咱們就能夠參考他的註解@EnableConfigurationProperties指定的properties類HttpProperties
@ConfigurationProperties(
    prefix = "spring.http"
)
public class HttpProperties {

配置精髓

  1. springboot啓動會加載大量的自動配置類;
  2. 咱們看咱們須要的功能有沒有springboot默認寫好的自動配置類;
  3. 咱們再來看這個自動配置類中到底配了那些組件,假若已經有了,咱們就不須要再編寫配置文件進行配置了。
  4. 給容器中自動配置類添加組件的時候,會從對應的Properties類中獲取某些屬性。咱們就能夠在配置文件中指定這些屬性的值(其已經經過註解進行了綁定);

總結

xxxAutoConfiguration 這種類就是用來作自動配置的,他會給容器中添加相關的組件,其對應的Properties則對應了配置的各類屬性;也就是說,經過這些配置類,咱們之前須要在SpringMVC中寫配置類、文件實現的東西,如今,只須要在Properites配置文件中加入相關的配置便可,再也不那麼麻煩了。

固然,一些特殊的配置仍是得本身寫組件的哦。

@Conditional*相關注解

@Conditinal派生註解

只有其指定的條件成立,配置類的全部類型纔會生效,更小範圍的,例如註釋在某個Bean組件上面的相關條件註解成立,纔會生成該Bean。

經常使用派生註解一覽

@Conditional擴展註解 做用(判斷是否知足當前指定條件)
@ConditionalOnJava 系統的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean
@ConditionalOnExpression 知足SpEL表達式指定
@ConditionalOnClass 系統中有指定的類
@ConditionalOnMissingClass 系統中沒有指定的類
@ConditionalOnSingleCandidate 容器中只有一個指定的Bean,或者這個Bean是首選Bean
@ConditionalOnProperty 系統中指定的屬性是否有指定的值
@ConditionalOnResource 類路徑下是否存在指定資源文件
@ConditionalOnWebApplication 當前是web環境
@ConditionalOnNotWebApplication 當前不是web環境
@ConditionalOnJndi JNDI存在指定項

咱們能夠發現,儘管咱們擁有許多的自動配置類,其仍是得必須知足必定條件纔會生效,該機制就是有@Conditinal派生註解等控制的,常見的是@ConditionalOnClass,即判斷當前系統具不具有相關的類。

debug模式

配置文件application.properties中添加配置,開啓Debug模式:

debug=true

默認狀況下debug的值爲false,經過啓用debug=true屬性,讓控制檯打印相關的報告,例如那些自動配置類啓用(positive matches)、沒啓用(negative matches)等。

相關文章
相關標籤/搜索