Springboot 配置 自動裝配原理


原理初探

pom.xml

  • spring-boot-dependencies:核心依賴在父工程中
  • 我們在寫或者引入一些Springboot依賴的時候,不需要指定版本,就因為有這些版本倉庫

啟動器

  • 通式

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-xxx</artifactId>
    </dependency>
    
  • 啟動器:說白了就是springboot的啟動場景

  • 比如,spring-boot-starter-web,他就會幫我們自動導入web環境所有的依賴

  • springboot會將所有的功能場景,都變成一個個的啟動器

  • 我們要使用什么功能,就只需要找到對應的啟動器就可以了starter

主程序

@SpringBootApplication //標注這個類是一個springboot的應用
public class HelloworldApplication {

	public static void main(String[] args) {
		//將springboot應用啟動
		SpringApplication.run(HelloworldApplication.class, args);
	}
}
  • 注解
@SpringBootConfiguration:springboot的配置
	@Configuration:spring配置類
	@Component:說明這也是一個spring的組件
@EnableAutoConfiguration:自動配置
	@AutoConfigurationPackage:自動配置包
		@Import({Registrar.class}):導入選擇器,包注冊
	@Import({AutoConfigurationImportSelector.class}):自動配置導入選擇
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //獲取所有的配置

獲取候選的配置

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

META-INF/spring.factories:自動配置的核心文件

Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//所有資源加載到配置類中!

結論:springboot所有自動配置都是在啟動的時候掃描並加載:spring.factories所有的自動配置類都在這里面,但是不一定生效,要判斷條件是否成立,只要導入了對應的start,就有對應的啟動器了,有了啟動器,我們自動裝配就會生效,然后就配置成功!

  1. springboot在啟動的時候,從類路徑下 /MEAT-INF/spring.factories獲取指定的值
  2. 將這些自動配置的類導入容器,自動配置就會生效,幫我進行自動配置
  3. 以前我們需要自動配置的東西,現在 springboot 幫我們做了
  4. 整個javaEE,解決方案和自動配置的東西都在 spring-boot-autoconfigure-2.2.0.RELEASE.jar 這個包下
  5. 它會把所有需要導入的組件,以類名的方式返回,這些組件就會被添加到容器
  6. 容器中也會存在非常多的 xxxAutoConfiguration 的文件(@Bean),就是這些類給容器中導入了這個場景需要的所有組件;並自動配置,@Configuration,JavaConfig!
  7. 有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作

SpringApplication這個類(加了@SpringApplication注解的主類)主要做了以下四件事情:

  1. 推斷應用的類型是普通的項目還是Web項目
  2. 查找並加載所有可用初始化器, 設置到 initializers 屬性中
  3. 找出所有的應用程序監聽器,設置到 listeners 屬性中
  4. 推斷並設置main方法的定義類,找到運行的主類

springboot配置

配置文件

SpringBoot使用一個全局的配置文件,配置文件名稱是固定的

  • application.properties
    語法結構:key=value
  • application.yaml
    。語法結構:key: 空格value

配置文件的作用:修改SpringBoot自動配置的默認值,因為SpringBoot在底層都給我們自動配置好了

#springboot配置文件都能配置什么東西呢?

#官方文檔:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-external-config
#Part X. Appendices Appendix A. Common application properties

#官方的配置太多了,了解原理

##基本語法,對空格縮進要求很高

#普通的key-value
name: peng

#存一個對象
student:
  name: peng
  age: 18

#行內寫法
student: {name: peng,age: 18}

#數組
pets:
  - cat
  - dog

pet: [cat,dog,pig]

yaml 可以直接給實體類賦值

yaml配置類的屬性

application.yaml 配置

person:
  name: peng${random.uuid}
  age: 18
  happy: false
  birth: 2001/06/03
  maps: {k1: v1,k2: v2}
  lists: [eat,sleep,girl]
  dog:
    name: ${person.name:沒有主人}的狗 #冒號后面的是默認值
    age: ${random.int(1,5)}

Person.java Person類

package com.peng.pojo;

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

import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "person") //綁定yanl,讓yaml生效
public class Person {
    private String name;
    private Integer age;
    private boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;

    public Person() {
    }

    public Person(String name, Integer age, boolean happy, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
        this.name = name;
        this.age = age;
        this.happy = happy;
        this.birth = birth;
        this.maps = maps;
        this.lists = lists;
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public boolean isHappy() {
        return happy;
    }

    public void setHappy(boolean happy) {
        this.happy = happy;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

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

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

    public List<Object> getLists() {
        return lists;
    }

    public void setLists(List<Object> lists) {
        this.lists = lists;
    }

    public Dog getDog() {
        return dog;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", happy=" + happy +
                ", birth=" + birth +
                ", maps=" + maps +
                ", lists=" + lists +
                ", dog=" + dog +
                '}';
    }
}

Spring02ConfigApplicationTest.java 測試類

package com.peng;

import com.peng.pojo.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class Spring02ConfigApplicationTest {

    @Autowired
    private Person person;

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

輸出:

Person{name='peng24756efe-3e05-4d61-abe7-f31e59e94f6f', age=18, happy=false, birth=Sun Jun 03 00:00:00 CST 2001, maps={k1=v1, k2=v2}, lists=[eat, sleep, girl], dog=Dog{name='peng92a5f5f2-3149-4336-9c45-76da5e680285的狗', age=3}}

  • 松散綁定

    lastName,在yaml中可以寫成last-name

  • JSR303校驗:賦值前驗證數據類型

    @Validated //數據校驗
    public class Person {
        @NotNull //傳入的值必須不為空才能通過驗證
        private String name;
    }
    

    Bean Validation 中內置的 constraint

Hibernate Validator 附加的 constraint

多環境配置

#springboot的多環境配置:可以選擇激活哪一個配置文件
server:
  port: 8080
spring:
  profiles:
    active: dev

# --- 分文檔模塊
---
server:
  port: 8081
spring:
  profiles: dev
---
server:
  port: 8082
spring:
  profiles: test

淺顯的底層原理

# 配置文件里到底能寫什么 --聯系-- spring.factories
# 在我們這配置文件中能配置的東西,都存在一個固有的規律
# xxxAutoConfiguration --連接-- xxxProperties --連接-- yaml
# xxxAutoConfiguration:自動裝配的默認值
# xxxProperties有set方法改變默認值,和配置文件yaml綁定,設置新值並使用
server:
  port: 8080

ctrl+鼠標左鍵,點進 port 看源碼,果然有 port 屬性和 set 方法

private Integer port;

public void setPort(Integer port) {
    this.port = port;
}

發現這個方法屬於 ServerProperties 類,類上有注解 @ConfigurationProperties,他連接 server 的默認配置文件

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties{...}

自動默認配置文件中果然有

點進去之后,確實是個自動配置類,在注解中,他允許了 ServerProperties 類重新設置 port 等屬性,這些設置可通過配置文件 yaml 配置

而 ServerProperties 類連接 yaml,就是通過之前說過的 @ConfigurationProperties 注解,點進 prefix,是一個配置屬性的注解

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

本質:我們原先需要在 bean 中配置的屬性(properties)封裝成一個類然后通過 yaml 文件進行自動注入,而我們可以在 application.yaml 文件中自定義這些 property 屬性

  • 自動裝配原理總結

    1. SpringBoot 啟動會加載大量的自動配置類

    2. 我們看我們需要的功能有沒有在SpringBoot默認寫好的自動配置類當中

    3. 我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)

    4. 給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可

      xxxAutoConfigurartion:自動配置類;給容器中添加組件

      xxxProperties:封裝配置文件中相關屬性;

  • 通過 debug=true 來查看,哪些配置生效,哪些沒有

    debug: true
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM