一、Spring Boot測試與JUnit5
1.1、Spring Boot Test介紹
Spring Test與JUnit等其他測試框架結合起來,提供了便捷高效的測試手段。而Spring Boot Test 是在Spring Test之上的再次封裝,增加了切片測試,增強了mock能力。
整體上,Spring Boot Test支持的測試種類,大致可以分為如下三類:
單元測試:一般面向方法,編寫一般業務代碼時,測試成本較大。涉及到的注解有@Test。
切片測試:一般面向難於測試的邊界功能,介於單元測試和功能測試之間。涉及到的注解有@RunWith @WebMvcTest等。
功能測試:一般面向某個完整的業務功能,同時也可以使用切面測試中的mock能力,推薦使用。涉及到的注解有@RunWith @SpringBootTest等。
功能測試過程中的幾個關鍵要素及支撐方式如下:
測試運行環境:通過@RunWith 和 @SpringBootTest啟動spring容器。
mock能力:Mockito提供了強大mock功能。
斷言能力:AssertJ、Hamcrest、JsonPath提供了強大的斷言能力。
1.2、Junit介紹
JUnit是一個Java語言的單元測試框架。它由Kent Beck和Erich Gamma建立,逐漸成為源於Kent Beck的sUnit的xUnit家族中最為成功的一個JUnit有它自己的JUnit擴展生態圈。多數Java的開發環境都已經集成了JUnit作為單元測試的工具。
Junit 測試也是程序員測試,即所謂的白盒測試,它需要程序員知道被測試的代碼如何完成功能,以及完成什么樣的功能
我們知道 Junit 是一個單元測試框架,那么使用 Junit 能讓我們快速的完成單元測試。
通常我們寫完代碼想要測試這段代碼的正確性,那么必須新建一個類,然后創建一個 main() 方法,然后編寫測試代碼。如果需要測試的代碼很多呢?那么要么就會建很多main() 方法來測試,要么將其全部寫在一個 main() 方法里面。這也會大大的增加測試的復雜度,降低程序員的測試積極性。而 Junit 能很好的解決這個問題,簡化單元測試,寫一點測一點,在編寫以后的代碼中如果發現問題可以較快的追蹤到問題的原因,減小回歸錯誤的糾錯難度。
1.3、Spring Boot中使用test
1.3.1、添加依賴
增加spring-boot-starter-test依賴,使用@RunWith和@SpringBootTest注解,即可開始測試。
pom中添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
一旦依賴了spring-boot-starter-test,下面這些類庫將被一同依賴進去:
Spring Boot2.66的測試起步依賴的pom文件如下:

<?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- This module was also published with a richer model, Gradle metadata, --> <!-- which should be used instead. Do not delete the following line which --> <!-- is to indicate to Gradle or any Gradle module metadata file consumer --> <!-- that they should prefer consuming it instead. --> <!-- do_not_remove: published-with-gradle-metadata --> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>2.6.6</version> <name>spring-boot-starter-test</name> <description>Starter for testing Spring Boot applications with libraries including JUnit Jupiter, Hamcrest and Mockito</description> <url>https://spring.io/projects/spring-boot</url> <organization> <name>Pivotal Software, Inc.</name> <url>https://spring.io</url> </organization> <licenses> <license> <name>Apache License, Version 2.0</name> <url>https://www.apache.org/licenses/LICENSE-2.0</url> </license> </licenses> <developers> <developer> <name>Pivotal</name> <email>info@pivotal.io</email> <organization>Pivotal Software, Inc.</organization> <organizationUrl>https://www.spring.io</organizationUrl> </developer> </developers> <scm> <connection>scm:git:git://github.com/spring-projects/spring-boot.git</connection> <developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git</developerConnection> <url>https://github.com/spring-projects/spring-boot</url> </scm> <issueManagement> <system>GitHub</system> <url>https://github.com/spring-projects/spring-boot/issues</url> </issueManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.6.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <version>2.6.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test-autoconfigure</artifactId> <version>2.6.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <version>2.6.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>2.3.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.21.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest</artifactId> <version>2.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.8.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.0.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>4.0.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.skyscreamer</groupId> <artifactId>jsonassert</artifactId> <version>1.5.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.18</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.18</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.xmlunit</groupId> <artifactId>xmlunit-core</artifactId> <version>2.8.4</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>jaxb-api</artifactId> <groupId>javax.xml.bind</groupId> </exclusion> </exclusions> </dependency> </dependencies> </project>
從依賴的坐標可以看出是junit-jupiter(junit5.0+)
JUnit:Java測試事實上的標准,默認依賴版本與SpringBoot版本相關(JUnit5和JUnit4差別比較大,集成方式有不同)。
Spring Test & Spring Boot Test:Spring的測試支持。
AssertJ:提供了流式的斷言方式。
Hamcrest:提供了豐富的matcher。
Mockito:mock框架,可以按類型創建mock對象,可以根據方法參數指定特定的響應,也支持對於mock調用過程的斷言。
JSONassert:為JSON提供了斷言功能。
JsonPath:為JSON提供了XPATH功能。
1.3.2、注解測試類
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest //為測試提供上下文環境 class UsermisApplicationTests { @Test //聲明需測試的方法 void testEqual(){ int actual=1; //斷言actual是否與1相等 Assertions.assertEquals(1,actual); } }
右鍵運行測試的結果:
1.4、常用注解
1.4.1、SpringBootTest
@SpringBootTest 指定測試的啟動類
- args = “–app.test=one” 指定參數
- classes 指定啟動類,可以是多個
- value 指定配置屬性
- properties 指定配置屬性,和value相同
- webEnvironment 指定web環境,
- MOCK 此值為默認值,該類型提供一個mock環境,此時內嵌的服務(servlet容器)並沒有真正啟動,也不會監聽web端口
- RANDOM_PORT 啟動一個真實的web服務,監聽一個隨機端口
- DEFINED_PORT 啟動一個真實的web服務,監聽一個定義好的端口(從配置中讀取)。
- NONE 啟動一個非web的ApplicationContext,既不提供mock環境,也不提供真是的web服務
1.4.2、@Test
@Test 指定測試方法
1.4.3、TestMethodOrder、Order 排序,指定先后順序
@TestMethodOrder(MethodOrderer.Alphanumeric.class)指定測試的順序,需要配合@Order一起使用
package com.zhangguo.springboottest; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) //設置排序模式 public class MyTests { @Test @Order(2) //設置排序值 public void testA(){ } @Test @Order(1) //設置排序值 public void testB(){ } }
運行效果:
1.4.4、BeforeEach、BeforeAll、AfterEach、AfterAll
@BeforeEach:在每個單元測試方法執行前都執行一遍
@BeforeAll:在所有單元測試方法執行前執行一遍(只執行一次),需靜態方法,除非將測試類注解@TestInstance
org.junit.platform.commons.JUnitException: @BeforeAll method 'public void com.gdnf.usermis.UsermisApplicationTests.beforeAll()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).
@AfterEach:在每個單元測試方法執行后都執行一遍
@AfterAll:在所有單元測試方法執行后執行一遍(只執行一次)
package com.zhangguo.springboottest; import org.junit.jupiter.api.*; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) //設置排序模式 public class MyTests { @BeforeAll public static void beforeAll(){ System.out.println("BeforeAll在所有單元測試方法執行前執行一遍(只執行一次)"); } @BeforeEach public void beforeEach(){ System.out.println("BeforeEach第個測試方法執行前先執行該方法"); } @AfterEach public void afterEach(){ System.out.println("AfterEach在每個單元測試方法執行后都執行一遍"); } @AfterAll public static void afterAll(){ System.out.println("AfterAll在所有單元測試方法執行后執行一遍(只執行一次)"); } @Test @Order(2) //設置排序值 public void testA(){ } @Test @Order(1) //設置排序值 public void testB(){ } }
運行結果:

BeforeAll在所有單元測試方法執行前執行一遍(只執行一次) . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.6) 2022-04-11 10:53:47.481 INFO 2224 --- [ main] com.zhangguo.springboottest.MyTests : Starting MyTests using Java 1.8.0_111 on PC201501031108 with PID 2224 (started by Administrator in E:\NF\Spring boot\demos\springboottest) 2022-04-11 10:53:47.484 INFO 2224 --- [ main] com.zhangguo.springboottest.MyTests : No active profile set, falling back to 1 default profile: "default" 2022-04-11 10:53:52.381 INFO 2224 --- [ main] com.zhangguo.springboottest.MyTests : Started MyTests in 6.147 seconds (JVM running for 8.933) BeforeEach第個測試方法執行前先執行該方法 AfterEach在每個單元測試方法執行后都執行一遍 BeforeEach第個測試方法執行前先執行該方法 AfterEach在每個單元測試方法執行后都執行一遍 AfterAll在所有單元測試方法執行后執行一遍(只執行一次) Process finished with exit code 0
@DisplayName("商品入庫測試"):用於指定單元測試的名稱
1.4.5、Disabled 禁用測試
@Disabled:當前單元測試置為無效,即單元測試時跳過該測試
1.4.6、RepeatedTest 重復測試
@RepeatedTest(n):重復性測試,即執行n次

package com.zhangguo.springboottest; import org.junit.jupiter.api.*; import org.springframework.boot.test.context.SpringBootTest; import java.util.Random; @SpringBootTest public class MyTests { Random random=new Random(); //隨機數 @RepeatedTest(3) //重復執行測試3次 public void testA(){ System.out.println(random.nextInt(100)); } @Test public void testB(){ } }
1.4.7、ParameterizedTest、ValueSource 參數化參數
@ParameterizedTest:參數化測試,
參數化測試可以按照多個參數分別運行多次單元測試這里有點類似於重復性測試,只不過每次運行傳入的參數不用。需要使用到@ParameterizedTest,同時也需要@ValueSource提供一組數據,它支持八種基本類型以及String和自定義對象類型,使用極其方便。

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.junit.jupiter.params.provider; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @API( status = Status.STABLE, since = "5.7" ) @ArgumentsSource(ValueArgumentsProvider.class) public @interface ValueSource { short[] shorts() default {}; byte[] bytes() default {}; int[] ints() default {}; long[] longs() default {}; float[] floats() default {}; double[] doubles() default {}; char[] chars() default {}; boolean[] booleans() default {}; String[] strings() default {}; Class<?>[] classes() default {}; }
@ValueSource(ints = {1, 2, 3}):參數化測試提供數據
package com.zhangguo.springboottest; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.test.context.SpringBootTest; import java.util.Random; @SpringBootTest public class MyTests { @ParameterizedTest //聲明該方法為參數化測試 @ValueSource(ints = {-1,0,1}) //參數源 public void testB(int a){ Assertions.assertTrue(a>=0); //如果結果為true測試測試通過 } }
運行結果:
1.5、斷言
Jupiter提供了強大的斷言方法用以驗證結果,在使用時需要借助java8的新特性lambda表達式,均是來自org.junit.jupiter.api.Assertions包的static方法。
1.5.1、assertTrue與assertFalse
assertTrue與assertFalse用來判斷條件是否為true或false,assertTrue表示如果值為true則通過,assertFalse表示如果值為false則通過
@Test @DisplayName("測試斷言equals") void testEquals() { assertTrue(3 < 4); }
1.5.2、assertNull與assertNotNull
assertNull與assertNotNull用來判斷條件是否為null
@Test @DisplayName("測試斷言NotNull") void testNotNull() { assertNotNull(new Object()); }

package com.zhangguo.springboottest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class AssertTest { static MyMath myMath; @BeforeAll public static void beforeAll(){ myMath=new MyMath(); } @Test public void testA(){ int a=10; /**如果為true則測試通過*/ Assertions.assertTrue(a>0&&a<99); } @Test public void testB(){ /**執行1加到n的方法獲取結果*/ int actual=myMath.addToN(100); /**如果結果為5050則測試通過*/ Assertions.assertEquals(5050,actual); } @Test public void testC(){ String str=null; Assertions.assertNull(str); //如果str為null則測試通過 } @Test public void testD(){ String str=null; Assertions.assertNotNull(str);//如果str不為null則測試通過 } }
結果:
1.5.3、assertThrows異常
assertThrows用來判斷執行拋出的異常是否符合預期,並可以使用異常類型接收返回值進行其他操作
lambda要求jdk8以上,如果報錯需要設置:錯誤一(Lambda expressions are not supported at language level '7'),錯誤二(源發行版 8 需要目標發行版 1.8)
測試通過:
@Test public void testE(){ Assertions.assertThrows(NullPointerException.class,()->{ String str=null; System.out.println(str.toLowerCase()); }); }
測試不通過:
@Test public void testE(){ Assertions.assertThrows(NullPointerException.class,()->{ String str="A"; System.out.println(str.toLowerCase()); }); }
1.5.4、assertTimeout超時
assertTimeout用來判斷執行過程是否超時
@Test public void testF(){ long actual=Assertions.assertTimeout(Duration.ofMillis(1000),()->{ Date start=new Date(); //記住開始時間 Thread.sleep(700); //休眠700毫秒 long span=new Date().getTime()-start.getTime(); //結束時間減去開始時間得到耗時 return span; //返回耗時時間 }); System.out.println(actual); //如果耗時小於1000毫秒則測試通過 Assertions.assertTrue(actual<=1000); }
因為耗時只有700+小於1000的最大耗時結果測試通過。
1.5.5、assertAll組合斷言
assertAll是組合斷言,當它內部所有斷言正確執行完才算通過
不通過示例:
@Test public void testG(){ String name=null; int age=17; //第一個參數是標題 Assertions.assertAll("模擬輸入",()->{ Assertions.assertNotNull(name); //name不為空 },()->{ Assertions.assertTrue(age>0); //age大於零 },()->{ Assertions.assertTrue(age<=130); //age小於130 }); //當3個子斷言都通過時則整體通過 }
通過示例:
@Test public void testG(){ String name="張果"; int age=19; //第一個參數是標題 Assertions.assertAll("模擬輸入",()->{ Assertions.assertNotNull(name); //name不為空 },()->{ Assertions.assertTrue(age>0); //age大於零 },()->{ Assertions.assertTrue(age<=130); //age小於130 }); //當3個子斷言都通過時則整體通過 }
1.5.6、其它斷言
這里不能把所有斷言方法都列出來,可以查看Assertions類的定義,或使用IDE的智能提示選擇:

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.junit.jupiter.api; import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import java.util.stream.Stream; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.function.ThrowingSupplier; import org.opentest4j.MultipleFailuresError; @API( status = Status.STABLE, since = "5.0" ) public class Assertions { @API( status = Status.STABLE, since = "5.3" ) protected Assertions() { } public static <V> V fail() { AssertionUtils.fail(); return null; } public static <V> V fail(String message) { AssertionUtils.fail(message); return null; } public static <V> V fail(String message, Throwable cause) { AssertionUtils.fail(message, cause); return null; } public static <V> V fail(Throwable cause) { AssertionUtils.fail(cause); return null; } public static <V> V fail(Supplier<String> messageSupplier) { AssertionUtils.fail(messageSupplier); return null; } public static void assertTrue(boolean condition) { AssertTrue.assertTrue(condition); } public static void assertTrue(boolean condition, Supplier<String> messageSupplier) { AssertTrue.assertTrue(condition, messageSupplier); } public static void assertTrue(BooleanSupplier booleanSupplier) { AssertTrue.assertTrue(booleanSupplier); } public static void assertTrue(BooleanSupplier booleanSupplier, String message) { AssertTrue.assertTrue(booleanSupplier, message); } public static void assertTrue(boolean condition, String message) { AssertTrue.assertTrue(condition, message); } public static void assertTrue(BooleanSupplier booleanSupplier, Supplier<String> messageSupplier) { AssertTrue.assertTrue(booleanSupplier, messageSupplier); } public static void assertFalse(boolean condition) { AssertFalse.assertFalse(condition); } public static void assertFalse(boolean condition, String message) { AssertFalse.assertFalse(condition, message); } public static void assertFalse(boolean condition, Supplier<String> messageSupplier) { AssertFalse.assertFalse(condition, messageSupplier); } public static void assertFalse(BooleanSupplier booleanSupplier) { AssertFalse.assertFalse(booleanSupplier); } public static void assertFalse(BooleanSupplier booleanSupplier, String message) { AssertFalse.assertFalse(booleanSupplier, message); } public static void assertFalse(BooleanSupplier booleanSupplier, Supplier<String> messageSupplier) { AssertFalse.assertFalse(booleanSupplier, messageSupplier); } public static void assertNull(Object actual) { AssertNull.assertNull(actual); } public static void assertNull(Object actual, String message) { AssertNull.assertNull(actual, message); } public static void assertNull(Object actual, Supplier<String> messageSupplier) { AssertNull.assertNull(actual, messageSupplier); } public static void assertNotNull(Object actual) { AssertNotNull.assertNotNull(actual); } public static void assertNotNull(Object actual, String message) { AssertNotNull.assertNotNull(actual, message); } public static void assertNotNull(Object actual, Supplier<String> messageSupplier) { AssertNotNull.assertNotNull(actual, messageSupplier); } public static void assertEquals(short expected, short actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(short expected, Short actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Short expected, short actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Short expected, Short actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(short expected, short actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(short expected, Short actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Short expected, short actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Short expected, Short actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(short expected, short actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(short expected, Short actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Short expected, short actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Short expected, Short actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(byte expected, byte actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(byte expected, Byte actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Byte expected, byte actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Byte expected, Byte actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(byte expected, byte actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(byte expected, Byte actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Byte expected, byte actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Byte expected, Byte actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(byte expected, byte actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(byte expected, Byte actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Byte expected, byte actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Byte expected, Byte actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(int expected, int actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(int expected, Integer actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Integer expected, int actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Integer expected, Integer actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(int expected, int actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(int expected, Integer actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Integer expected, int actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Integer expected, Integer actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(int expected, int actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(int expected, Integer actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Integer expected, int actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Integer expected, Integer actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(long expected, long actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(long expected, Long actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Long expected, long actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Long expected, Long actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(long expected, long actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(long expected, Long actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Long expected, long actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Long expected, Long actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(long expected, long actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(long expected, Long actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Long expected, long actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Long expected, Long actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(float expected, float actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(float expected, Float actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Float expected, float actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Float expected, Float actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(float expected, float actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(float expected, Float actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Float expected, float actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Float expected, Float actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(float expected, float actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(float expected, Float actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Float expected, float actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Float expected, Float actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(float expected, float actual, float delta) { AssertEquals.assertEquals(expected, actual, delta); } public static void assertEquals(float expected, float actual, float delta, String message) { AssertEquals.assertEquals(expected, actual, delta, message); } public static void assertEquals(float expected, float actual, float delta, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, delta, messageSupplier); } public static void assertEquals(double expected, double actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(double expected, Double actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Double expected, double actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Double expected, Double actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(double expected, double actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(double expected, Double actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Double expected, double actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Double expected, Double actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(double expected, double actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(double expected, Double actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Double expected, double actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Double expected, Double actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(double expected, double actual, double delta) { AssertEquals.assertEquals(expected, actual, delta); } public static void assertEquals(double expected, double actual, double delta, String message) { AssertEquals.assertEquals(expected, actual, delta, message); } public static void assertEquals(double expected, double actual, double delta, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, delta, messageSupplier); } public static void assertEquals(char expected, char actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(char expected, Character actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Character expected, char actual) { AssertEquals.assertEquals(expected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Character expected, Character actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(char expected, char actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(char expected, Character actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Character expected, char actual, String message) { AssertEquals.assertEquals(expected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Character expected, Character actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(char expected, char actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(char expected, Character actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Character expected, char actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertEquals(Character expected, Character actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertEquals(Object expected, Object actual) { AssertEquals.assertEquals(expected, actual); } public static void assertEquals(Object expected, Object actual, String message) { AssertEquals.assertEquals(expected, actual, message); } public static void assertEquals(Object expected, Object actual, Supplier<String> messageSupplier) { AssertEquals.assertEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(boolean[] expected, boolean[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(boolean[] expected, boolean[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(boolean[] expected, boolean[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(char[] expected, char[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(char[] expected, char[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(char[] expected, char[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(byte[] expected, byte[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(byte[] expected, byte[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(byte[] expected, byte[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(short[] expected, short[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(short[] expected, short[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(short[] expected, short[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(int[] expected, int[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(int[] expected, int[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(int[] expected, int[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(long[] expected, long[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(long[] expected, long[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(long[] expected, long[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(float[] expected, float[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(float[] expected, float[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(float[] expected, float[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(float[] expected, float[] actual, float delta) { AssertArrayEquals.assertArrayEquals(expected, actual, delta); } public static void assertArrayEquals(float[] expected, float[] actual, float delta, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, delta, message); } public static void assertArrayEquals(float[] expected, float[] actual, float delta, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, delta, messageSupplier); } public static void assertArrayEquals(double[] expected, double[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(double[] expected, double[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(double[] expected, double[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertArrayEquals(double[] expected, double[] actual, double delta) { AssertArrayEquals.assertArrayEquals(expected, actual, delta); } public static void assertArrayEquals(double[] expected, double[] actual, double delta, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, delta, message); } public static void assertArrayEquals(double[] expected, double[] actual, double delta, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, delta, messageSupplier); } public static void assertArrayEquals(Object[] expected, Object[] actual) { AssertArrayEquals.assertArrayEquals(expected, actual); } public static void assertArrayEquals(Object[] expected, Object[] actual, String message) { AssertArrayEquals.assertArrayEquals(expected, actual, message); } public static void assertArrayEquals(Object[] expected, Object[] actual, Supplier<String> messageSupplier) { AssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier); } public static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual) { AssertIterableEquals.assertIterableEquals(expected, actual); } public static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual, String message) { AssertIterableEquals.assertIterableEquals(expected, actual, message); } public static void assertIterableEquals(Iterable<?> expected, Iterable<?> actual, Supplier<String> messageSupplier) { AssertIterableEquals.assertIterableEquals(expected, actual, messageSupplier); } public static void assertLinesMatch(List<String> expectedLines, List<String> actualLines) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines); } public static void assertLinesMatch(List<String> expectedLines, List<String> actualLines, String message) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines, message); } public static void assertLinesMatch(List<String> expectedLines, List<String> actualLines, Supplier<String> messageSupplier) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines, messageSupplier); } public static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines); } public static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines, String message) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines, message); } public static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines, Supplier<String> messageSupplier) { AssertLinesMatch.assertLinesMatch(expectedLines, actualLines, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, byte actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, Byte actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, byte actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, Byte actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, byte actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, Byte actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, byte actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, Byte actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, byte actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(byte unexpected, Byte actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, byte actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Byte unexpected, Byte actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, short actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, Short actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, short actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, Short actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, short actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, Short actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, short actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, Short actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, short actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(short unexpected, Short actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, short actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Short unexpected, Short actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, int actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, Integer actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, int actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, Integer actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, int actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, Integer actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, int actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, Integer actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, int actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(int unexpected, Integer actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, int actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Integer unexpected, Integer actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, long actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, Long actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, long actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, Long actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, long actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, Long actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, long actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, Long actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, long actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(long unexpected, Long actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, long actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Long unexpected, Long actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, Float actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, float actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, Float actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, Float actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, float actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, Float actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, Float actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, float actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Float unexpected, Float actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual, float delta) { AssertNotEquals.assertNotEquals(unexpected, actual, delta); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual, float delta, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, delta, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(float unexpected, float actual, float delta, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, delta, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, Double actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, double actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, Double actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, Double actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, double actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, Double actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, Double actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, double actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Double unexpected, Double actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual, double delta) { AssertNotEquals.assertNotEquals(unexpected, actual, delta); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual, double delta, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, delta, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(double unexpected, double actual, double delta, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, delta, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, char actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, Character actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, char actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, Character actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, char actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, Character actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, char actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, Character actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, char actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(char unexpected, Character actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, char actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } @API( status = Status.STABLE, since = "5.4" ) public static void assertNotEquals(Character unexpected, Character actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } public static void assertNotEquals(Object unexpected, Object actual) { AssertNotEquals.assertNotEquals(unexpected, actual); } public static void assertNotEquals(Object unexpected, Object actual, String message) { AssertNotEquals.assertNotEquals(unexpected, actual, message); } public static void assertNotEquals(Object unexpected, Object actual, Supplier<String> messageSupplier) { AssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier); } public static void assertSame(Object expected, Object actual) { AssertSame.assertSame(expected, actual); } public static void assertSame(Object expected, Object actual, String message) { AssertSame.assertSame(expected, actual, message); } public static void assertSame(Object expected, Object actual, Supplier<String> messageSupplier) { AssertSame.assertSame(expected, actual, messageSupplier); } public static void assertNotSame(Object unexpected, Object actual) { AssertNotSame.assertNotSame(unexpected, actual); } public static void assertNotSame(Object unexpected, Object actual, String message) { AssertNotSame.assertNotSame(unexpected, actual, message); } public static void assertNotSame(Object unexpected, Object actual, Supplier<String> messageSupplier) { AssertNotSame.assertNotSame(unexpected, actual, messageSupplier); } public static void assertAll(Executable... executables) throws MultipleFailuresError { AssertAll.assertAll(executables); } public static void assertAll(String heading, Executable... executables) throws MultipleFailuresError { AssertAll.assertAll(heading, executables); } public static void assertAll(Collection<Executable> executables) throws MultipleFailuresError { AssertAll.assertAll(executables); } public static void assertAll(String heading, Collection<Executable> executables) throws MultipleFailuresError { AssertAll.assertAll(heading, executables); } public static void assertAll(Stream<Executable> executables) throws MultipleFailuresError { AssertAll.assertAll(executables); } public static void assertAll(String heading, Stream<Executable> executables) throws MultipleFailuresError { AssertAll.assertAll(heading, executables); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable) { return AssertThrowsExactly.assertThrowsExactly(expectedType, executable); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable, String message) { return AssertThrowsExactly.assertThrowsExactly(expectedType, executable, message); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier) { return AssertThrowsExactly.assertThrowsExactly(expectedType, executable, messageSupplier); } public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) { return AssertThrows.assertThrows(expectedType, executable); } public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, String message) { return AssertThrows.assertThrows(expectedType, executable, message); } public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier) { return AssertThrows.assertThrows(expectedType, executable, messageSupplier); } @API( status = Status.STABLE, since = "5.2" ) public static void assertDoesNotThrow(Executable executable) { AssertDoesNotThrow.assertDoesNotThrow(executable); } @API( status = Status.STABLE, since = "5.2" ) public static void assertDoesNotThrow(Executable executable, String message) { AssertDoesNotThrow.assertDoesNotThrow(executable, message); } @API( status = Status.STABLE, since = "5.2" ) public static void assertDoesNotThrow(Executable executable, Supplier<String> messageSupplier) { AssertDoesNotThrow.assertDoesNotThrow(executable, messageSupplier); } @API( status = Status.STABLE, since = "5.2" ) public static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier) { return AssertDoesNotThrow.assertDoesNotThrow(supplier); } @API( status = Status.STABLE, since = "5.2" ) public static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier, String message) { return AssertDoesNotThrow.assertDoesNotThrow(supplier, message); } @API( status = Status.STABLE, since = "5.2" ) public static <T> T assertDoesNotThrow(ThrowingSupplier<T> supplier, Supplier<String> messageSupplier) { return AssertDoesNotThrow.assertDoesNotThrow(supplier, messageSupplier); } public static void assertTimeout(Duration timeout, Executable executable) { AssertTimeout.assertTimeout(timeout, executable); } public static void assertTimeout(Duration timeout, Executable executable, String message) { AssertTimeout.assertTimeout(timeout, executable, message); } public static void assertTimeout(Duration timeout, Executable executable, Supplier<String> messageSupplier) { AssertTimeout.assertTimeout(timeout, executable, messageSupplier); } public static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier) { return AssertTimeout.assertTimeout(timeout, supplier); } public static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier, String message) { return AssertTimeout.assertTimeout(timeout, supplier, message); } public static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier, Supplier<String> messageSupplier) { return AssertTimeout.assertTimeout(timeout, supplier, messageSupplier); } public static void assertTimeoutPreemptively(Duration timeout, Executable executable) { AssertTimeout.assertTimeoutPreemptively(timeout, executable); } public static void assertTimeoutPreemptively(Duration timeout, Executable executable, String message) { AssertTimeout.assertTimeoutPreemptively(timeout, executable, message); } public static void assertTimeoutPreemptively(Duration timeout, Executable executable, Supplier<String> messageSupplier) { AssertTimeout.assertTimeoutPreemptively(timeout, executable, messageSupplier); } public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier) { return AssertTimeout.assertTimeoutPreemptively(timeout, supplier); } public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, String message) { return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, message); } public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, Supplier<String> messageSupplier) { return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, messageSupplier); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue) { return AssertInstanceOf.assertInstanceOf(expectedType, actualValue); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue, String message) { return AssertInstanceOf.assertInstanceOf(expectedType, actualValue, message); } @API( status = Status.EXPERIMENTAL, since = "5.8" ) public static <T> T assertInstanceOf(Class<T> expectedType, Object actualValue, Supplier<String> messageSupplier) { return AssertInstanceOf.assertInstanceOf(expectedType, actualValue, messageSupplier); } }
1.6、Mockito
Mockito 是一種 Java Mock 框架,主要是用來做 Mock 測試,它可以模擬任何 Spring 管理的 Bean、模擬方法的返回值、模擬拋出異常等等,避免你為了測試一個方法,卻要自行構建整個 bean 的依賴鏈。
像是 Mockito 可以在單元測試中模擬一個 Service 返回的數據,而不會真正去調用該 Service,這就是上面提到的 Mock 測試精神,也就是通過模擬一個假的 Service 對象,來快速的測試當前我想要測試的類。
目前在 Java 中主流的 Mock 測試工具有 Mockito、JMock、EasyMock等等,而 SpringBoot 目前內建的是 Mockito 框架。
mock:在軟件開發的世界之外, "mock"一詞是指模仿或者效仿。
因此可以將“mock”理解為一個替身,替代者。
在軟件開發中提及"mock",通常理解為模擬對象或者Fake
示例:
自定義控制器:
package com.zhangguo.springboottest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping("/mock") public String hello(@RequestParam(defaultValue = "") String name){ return "Hello "+name+"!"; } }
注意參數是動態的。
測試代碼:
package com.zhangguo.springboottest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @SpringBootTest @AutoConfigureMockMvc //啟用MockMVC自動配置 public class MVCTest { @Autowired MockMvc mockMvc; //自動裝配mockmvc對象,模擬mvc運行環境 @Test public void mvcTest() throws Exception { //模擬發送一個請求到/mock mockMvc.perform(MockMvcRequestBuilders .get("/mock") //設置請求地址 .param("name", "zhangguo")) //請求參數 .andExpect(MockMvcResultMatchers.status().isOk()) //期待狀態為200 .andExpect(MockMvcResultMatchers.content().string("Hello zhangguo!")) //期待請求返回的字符為Hello zhangguo! .andDo(MockMvcResultHandlers.print()) //將處理結果輸出到控制台 .andReturn(); //返回,可以繼續其它操作 } }
.perform() 執行一個MockMvcRequestBuilders請求。其中.get()表示發送get請求(可以使用get、post、put、delete等);.contentType()設置編碼格式;.param()請求參數,可以帶多個。
andExpect()添加 MockMvcResultMatchers驗證規則,驗證執行結果是否正確。
.andDo()添加 MockMvcResultHandlers結果處理器,這是可以用於打印結果輸出。
.andReturn()結果還回,然后可以進行下一步的處理。
測試結果

"C:\Program Files\Java\jdk1.8.0_111\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\lib\idea_rt.jar=2605:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\plugins\junit\lib\junit-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\plugins\junit\lib\junit5-rt.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\platform\junit-platform-launcher\1.8.2\junit-platform-launcher-1.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\platform\junit-platform-engine\1.8.2\junit-platform-engine-1.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\platform\junit-platform-commons\1.8.2\junit-platform-commons-1.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;E:\NF\Spring boot\demos\springboottest\target\test-classes;E:\NF\Spring boot\demos\springboottest\target\classes;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-web\2.6.6\spring-boot-starter-web-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter\2.6.6\spring-boot-starter-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-logging\2.6.6\spring-boot-starter-logging-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;H:\InstallFiles\javaKit\mavenRes\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;H:\InstallFiles\javaKit\mavenRes\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;H:\InstallFiles\javaKit\mavenRes\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;H:\InstallFiles\javaKit\mavenRes\org\yaml\snakeyaml\1.29\snakeyaml-1.29.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-json\2.6.6\spring-boot-starter-json-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-databind\2.13.2.2\jackson-databind-2.13.2.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-annotations\2.13.2\jackson-annotations-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-core\2.13.2\jackson-core-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.2\jackson-datatype-jdk8-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.2\jackson-datatype-jsr310-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.2\jackson-module-parameter-names-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-tomcat\2.6.6\spring-boot-starter-tomcat-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-core\9.0.60\tomcat-embed-core-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-el\9.0.60\tomcat-embed-el-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.60\tomcat-embed-websocket-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-web\5.3.18\spring-web-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-beans\5.3.18\spring-beans-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-webmvc\5.3.18\spring-webmvc-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-aop\5.3.18\spring-aop-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-context\5.3.18\spring-context-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-expression\5.3.18\spring-expression-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-test\2.6.6\spring-boot-starter-test-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-test\2.6.6\spring-boot-test-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-test-autoconfigure\2.6.6\spring-boot-test-autoconfigure-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\com\jayway\jsonpath\json-path\2.6.0\json-path-2.6.0.jar;H:\InstallFiles\javaKit\mavenRes\net\minidev\json-smart\2.4.8\json-smart-2.4.8.jar;H:\InstallFiles\javaKit\mavenRes\net\minidev\accessors-smart\2.4.8\accessors-smart-2.4.8.jar;H:\InstallFiles\javaKit\mavenRes\org\ow2\asm\asm\9.1\asm-9.1.jar;H:\InstallFiles\javaKit\mavenRes\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;H:\InstallFiles\javaKit\mavenRes\jakarta\xml\bind\jakarta.xml.bind-api\2.3.3\jakarta.xml.bind-api-2.3.3.jar;H:\InstallFiles\javaKit\mavenRes\jakarta\activation\jakarta.activation-api\1.2.2\jakarta.activation-api-1.2.2.jar;H:\InstallFiles\javaKit\mavenRes\org\assertj\assertj-core\3.21.0\assertj-core-3.21.0.jar;H:\InstallFiles\javaKit\mavenRes\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\jupiter\junit-jupiter\5.8.2\junit-jupiter-5.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\jupiter\junit-jupiter-api\5.8.2\junit-jupiter-api-5.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\jupiter\junit-jupiter-params\5.8.2\junit-jupiter-params-5.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\junit\jupiter\junit-jupiter-engine\5.8.2\junit-jupiter-engine-5.8.2.jar;H:\InstallFiles\javaKit\mavenRes\org\mockito\mockito-core\4.0.0\mockito-core-4.0.0.jar;H:\InstallFiles\javaKit\mavenRes\net\bytebuddy\byte-buddy\1.11.22\byte-buddy-1.11.22.jar;H:\InstallFiles\javaKit\mavenRes\net\bytebuddy\byte-buddy-agent\1.11.22\byte-buddy-agent-1.11.22.jar;H:\InstallFiles\javaKit\mavenRes\org\objenesis\objenesis\3.2\objenesis-3.2.jar;H:\InstallFiles\javaKit\mavenRes\org\mockito\mockito-junit-jupiter\4.0.0\mockito-junit-jupiter-4.0.0.jar;H:\InstallFiles\javaKit\mavenRes\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;H:\InstallFiles\javaKit\mavenRes\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-core\5.3.18\spring-core-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-jcl\5.3.18\spring-jcl-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-test\5.3.18\spring-test-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\xmlunit\xmlunit-core\2.8.4\xmlunit-core-2.8.4.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit5 com.zhangguo.springboottest.MVCTest,mvcTest 10:59:00.689 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate] 10:59:00.715 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)] 10:59:00.820 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.zhangguo.springboottest.MVCTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper] 10:59:00.851 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.zhangguo.springboottest.MVCTest], using SpringBootContextLoader 10:59:00.864 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.zhangguo.springboottest.MVCTest]: class path resource [com/zhangguo/springboottest/MVCTest-context.xml] does not exist 10:59:00.866 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.zhangguo.springboottest.MVCTest]: class path resource [com/zhangguo/springboottest/MVCTestContext.groovy] does not exist 10:59:00.866 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.zhangguo.springboottest.MVCTest]: no resource found for suffixes {-context.xml, Context.groovy}. 10:59:00.868 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.zhangguo.springboottest.MVCTest]: MVCTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 10:59:01.021 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.zhangguo.springboottest.MVCTest] 10:59:01.249 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [E:\NF\Spring boot\demos\springboottest\target\classes\com\zhangguo\springboottest\SpringboottestApplication.class] 10:59:01.254 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.zhangguo.springboottest.SpringboottestApplication for test class com.zhangguo.springboottest.MVCTest 10:59:01.553 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.zhangguo.springboottest.MVCTest]: using defaults. 10:59:01.554 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] 10:59:01.600 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource] 10:59:01.605 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute] 10:59:01.606 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@6440112d, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@31ea9581, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@231f98ef, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@7c137fd5, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@183ec003, org.springframework.test.context.support.DirtiesContextTestExecutionListener@7d9d0818, org.springframework.test.context.event.EventPublishingTestExecutionListener@221a3fa4, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@451001e5, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@2b40ff9c, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@3e08ff24, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@4d1c005e, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@8462f31, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@24569dba] 10:59:01.617 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@6d60fe40 testClass = MVCTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@792b749c testClass = MVCTest, locations = '{}', classes = '{class com.zhangguo.springboottest.SpringboottestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@23e84203 key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6c64cb25, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@5c5eefef, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@7f0eb4b4, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@223191a6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@74f0ea28, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@7921b0a2], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true]], class annotated with @DirtiesContext [false] with mode [null]. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.6) 2022-04-12 10:59:07.515 INFO 7828 --- [ main] com.zhangguo.springboottest.MVCTest : Starting MVCTest using Java 1.8.0_111 on PC201501031108 with PID 7828 (started by Administrator in E:\NF\Spring boot\demos\springboottest) 2022-04-12 10:59:07.536 INFO 7828 --- [ main] com.zhangguo.springboottest.MVCTest : The following 1 profile is active: "pro" 2022-04-12 10:59:20.170 INFO 7828 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet '' 2022-04-12 10:59:20.170 INFO 7828 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet '' 2022-04-12 10:59:20.174 INFO 7828 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 3 ms 2022-04-12 10:59:20.308 INFO 7828 --- [ main] com.zhangguo.springboottest.MVCTest : Started MVCTest in 18.562 seconds (JVM running for 21.385) MockHttpServletRequest: HTTP Method = GET Request URI = /mock Parameters = {name=[zhangguo]} Headers = [] Body = null Session Attrs = {} Handler: Type = com.zhangguo.springboottest.HelloController Method = com.zhangguo.springboottest.HelloController#hello(String) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"15"] Content type = text/plain;charset=UTF-8 Body = Hello zhangguo! Forwarded URL = null Redirected URL = null Cookies = [] Process finished with exit code 0
二、SpringBoot 配置
SpringBoot是基於約定的,所以很多配置都有默認值,但如果想使用自己的配置替換默認配置的話,就可以使用 application.properties或者application.yml(application.yaml)進行配置。
2.1、配置文件分類
- properties:
server.port=8080
- yml:
server:
port: 8080
-
SpringBoot提供了2種配置文件類型:properteis和yml/yaml
-
默認配置文件名稱:application
-
在同一級目錄下優先級為:properties > yml > yaml
2.2、YAML
- YAML全稱是 YAML Ain't Markup Language 。
- YAML是一種直觀的能夠被電腦識別的的數據序列化格式,並且容易被人類閱讀,容易和腳本語言交互的,可以被支持YAML庫的不同的編程語言程序導入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP 等。
- YML文件是以數據為核心的,比傳統的xml方式更加簡潔。
- YAML文件的擴展名可以使用.yml或者.yaml。
2.2.1、3種常用配置格式比較
properties
server.port=8082 server.address=127.0.0.1
yml:
server: port: 8081 address: 127.0.0.1
xml:
<server> <port>8080</port> <address>127.0.0.1</address> </server>
可見yml格式更加簡潔,以數據為核心。
2.2.2、YAML格式的基本語法
-
大小寫敏感
-
數據值前邊必須有空格,作為分隔符
-
使用縮進表示層級關系
-
縮進時不允許使用Tab鍵,只允許使用空格(各個系統 Tab對應的 空格數目可能不同,導致層次混亂)。
-
縮進的空格數目不重要,只要相同層級的元素左側對齊即可
-
#
表示注釋,從這個字符一直到行尾,都會被解析器忽略。
server: port: 8081 address: 127.0.0.1 name: zhang
2.2.3、YAML數據格式
- 對象(map):鍵值對的集合。
server: port: 8082 name: zhang name2: wang #對象 person: name2: ${name} name: li age: 20 gender: 男 address: - beijing - shanghai #對象行內寫法 person2: { name: zhao,age: 21 }
- 數組:一組按次序排列的值
#數組
address:
- beijing
- shanghai
#數組 行內寫法
address2: [ beijing,shanghai ]
- 純量(常量):單個的、不可再分的值 (雙引號原樣輸出,不轉義)
#純量、常量
msg1: 'hello \n world'
msg2: "hello \n world"
- yml 多文檔格式
# yml 多文檔 格式 --- 進行分隔 配置特定環境
#激活環境 spring: profiles: active: pro --- #開發環境 server: port: 8081 spring: config: activate: on-profile: dev --- #測試環境 server: port: 8082 spring: config: activate: on-profile: test --- #生產環境 server: port: 8083 spring: config: activate: on-profile: pro
2.2.4、YAML參數引用
name: zhang
name2: zhang
#對象
person:
name2: ${name} # 引用上邊定義的name值
name: zhang
2.2.5、YAML小結
-
配置文件類型
- properties:和以前一樣
- yml/yaml:注意空格
-
yaml:
簡潔,以數據為核心
-
基本語法
-
大小寫敏感
-
數據值前邊必須有空格,作為分隔符
-
使用空格縮進表示層級關系,相同縮進表示同一級
-
-
數據格式
-
對象
-
數組: 使用 “- ”表示數組每個元素
-
純量(常量)
-
-
參數引用
- ${key}
-
2.3、讀取配置內容
SpringBoot分別提供3中方式讀取項目的application.properties配置文件的內容。這個方式分別為:Environment類、@Value注解以及@ConfigurationProperties注解。
2.3.1、使用@Value注解讀取
package com.zhangguo.springboottest; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ParamController { @Value("${name}") String name; @Value("${server.port}") String port; @RequestMapping("/param") public String param(){ return "Hello "+name+" from "+port+"!"; } }
配置文件:
server: port: 8088 spring: config: activate: on-profile: pro name: zhangguo age: 18
運行結果:
其實@Value底層就是Environment
2.3.2、Environment
application.yaml
#1、map,鍵值對,對象,自定義 name: zhangguo #鍵值對 age: 19 superman: #對象 name: jack #引用 age: 88 sex: 男 address: - zhuhai - guangzhou #對象的行內寫法 superstar: {name: tom, sex: 男, address: [zhuhai,guangzhou] } #系統預定義配置 server: compression: enabled: true port: 8899 #字面量 info: 'Hello \n yaml' #轉義,換行 msg: "Hello \n msg" #原樣輸入
EvnController:
package com.zhangguo.configdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Properties; import java.util.Set; @RestController public class EvnController { @Autowired Environment env; //自動裝配對象 @RequestMapping("/env") public String config(){ String name=env.getProperty("name"); String port=env.getProperty("server.port"); String java_home=env.getProperty("JAVA_HOME"); String java_version=env.getProperty("java.runtime.version"); return "port="+port+",name="+name+",java_home="+java_home+",java_version="+java_version; } public static void main(String[] args) { Properties properties = System.getProperties(); //獲取系統中所有配置 Set<String> strings = properties.stringPropertyNames(); for (String key : strings) { System.out.println(key+"===>"+properties.get(key)); } } }
運行結果:
2.3.3、@ConfigurationProperties
首先建立一個Person類,定義姓名年齡,重寫tostring方法
寫入兩個注解
@Component //讓Springboot 識別這是一個bean @ConfigurationProperties // 讀取配置文件中的 name和age public class Person { private String name; private int age; }
在控制器類中用@AutoWired讀入Person
@Autowired private Person per;
該配置方法只能讀到第一層的name與age數據。如果想讀取配置文件中次級目錄如Person分級下,需要在person類中的@ConfigurationProperties設置分級。
// 讀取配置文件前綴為person鍵對應的對象中的 name和age @ConfigurationProperties(prefix = "person") public class Person { private String name; private int age; }
配置后在控制器中的
@Autowired private Person per; //輸出就是配置中person對象內的字段信息了 System.out.println(per.getName()+" "+per.getAge());
示例:
Man實體類:

package com.zhangguo.configdemo.entity; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.io.Serializable; @Component //讓Spring容器發現該對象 @ConfigurationProperties(prefix = "superman") //映射前綴為superman的配置信息 public class Man implements Serializable { private String name; private int 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; } }
控制器:
package com.zhangguo.configdemo.controller; import com.zhangguo.configdemo.entity.Man; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Properties; import java.util.Set; @RestController public class ManController { @Autowired Man man; //自動裝配對象 @RequestMapping("/man") public Man config(){ return man; } }
運行結果:
小結:
1、@Environment @Value 不具有面向對象的特征,屬性過多就不方便管理和控制,太多就會現得雜亂無章。而且也沒用自動提示的功能。
2、@ConfigurationProperties 就是面向對象的機制,可以自動提示。所以底層springboto用的是@ConfigurationProperties
2.4、profile
我們在開發Spring Boot應用時,通常同一套程序會被安裝到不同環境,比如:開發、測試、生產等。其中數據庫地址、服務 器端口等等配置都不同,如果每次打包時,都要修改配置文件,那么非常麻煩。profile功能就是來進行動態配置切換的。
profile是用來完成不同環境下,配置動態切換功能的。
2.4.1、profile配置方式
- 多profile文件方式:提供多個配置文件,每個代表一種環境
- application-dev.properties/yml 開發環境
- application-test.properties/yml 測試環境
- application-pro.properties/yml 生產環境
- yml多文檔方式:
- 在yml中使用 --- 分隔不同配置
示例一:多文檔方式
application-test.yaml:
#測試環境 server: port: 8081 name: 測試環境
application-dev.yaml:
#開發環境 server: port: 8082 name: 開發環境
application-pro.yaml:
#運行環境 server: port: 8083 name: 運行環境
application.yaml:
#在配置文件中激活profile
spring:
profiles:
active: test
運行時控制台信息:

"C:\Program Files\Java\jdk1.8.0_111\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1625 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\lib\idea_rt.jar=1626:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.7\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;E:\NF\Spring boot\demos\profiledemo\target\classes;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-web\2.6.6\spring-boot-starter-web-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter\2.6.6\spring-boot-starter-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-logging\2.6.6\spring-boot-starter-logging-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;H:\InstallFiles\javaKit\mavenRes\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;H:\InstallFiles\javaKit\mavenRes\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;H:\InstallFiles\javaKit\mavenRes\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;H:\InstallFiles\javaKit\mavenRes\org\yaml\snakeyaml\1.29\snakeyaml-1.29.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-json\2.6.6\spring-boot-starter-json-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-databind\2.13.2.2\jackson-databind-2.13.2.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-annotations\2.13.2\jackson-annotations-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\core\jackson-core\2.13.2\jackson-core-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.2\jackson-datatype-jdk8-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.2\jackson-datatype-jsr310-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.2\jackson-module-parameter-names-2.13.2.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\boot\spring-boot-starter-tomcat\2.6.6\spring-boot-starter-tomcat-2.6.6.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-core\9.0.60\tomcat-embed-core-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-el\9.0.60\tomcat-embed-el-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.60\tomcat-embed-websocket-9.0.60.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-web\5.3.18\spring-web-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-beans\5.3.18\spring-beans-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-webmvc\5.3.18\spring-webmvc-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-aop\5.3.18\spring-aop-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-context\5.3.18\spring-context-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-expression\5.3.18\spring-expression-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-core\5.3.18\spring-core-5.3.18.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-jcl\5.3.18\spring-jcl-5.3.18.jar" com.zhangguo.profiledemo.ProfiledemoApplication . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.6) 2022-04-14 11:05:49.576 INFO 7512 --- [ main] c.z.profiledemo.ProfiledemoApplication : Starting ProfiledemoApplication using Java 1.8.0_111 on PC201501031108 with PID 7512 (E:\NF\Spring boot\demos\profiledemo\target\classes started by Administrator in E:\NF\Spring boot\demos\profiledemo) 2022-04-14 11:05:49.597 INFO 7512 --- [ main] c.z.profiledemo.ProfiledemoApplication : The following 1 profile is active: "test" 2022-04-14 11:05:51.660 INFO 7512 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http) 2022-04-14 11:05:51.679 INFO 7512 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-04-14 11:05:51.679 INFO 7512 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.60] 2022-04-14 11:05:51.974 INFO 7512 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-04-14 11:05:51.975 INFO 7512 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2255 ms 2022-04-14 11:05:52.596 INFO 7512 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path '' 2022-04-14 11:05:52.610 INFO 7512 --- [ main] c.z.profiledemo.ProfiledemoApplication : Started ProfiledemoApplication in 3.898 seconds (JVM running for 5.88) 2022-04-14 11:06:31.920 INFO 7512 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-04-14 11:06:31.920 INFO 7512 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-04-14 11:06:31.924 INFO 7512 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
控制器:
package com.zhangguo.profiledemo; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ProfileController { @Value("${name}") private String name; @RequestMapping("/profile") public String profile(){ return name; } }
運行結果:
示例二:單文檔方式
application.yaml
#在配置文件中激活profile spring: profiles: active: dev --- #測試環境 server: port: 8886 name: 測試環境 spring: config: activate: on-profile: test #當前偏好名稱 --- #開發環境 server: port: 8887 name: 開發環境 spring: config: activate: on-profile: dev #當前偏好名稱 --- #生產環境 server: port: 8888 name: 生產環境 spring: config: activate: on-profile: pro #當前偏好名稱
激活的方法見2.4.2,運行結果如下:
2.4.2、profile激活方式
- 一、配置文件: 再配置文件中配置:spring.profiles.active=dev
- 二、虛擬機參數:在VM options 指定:-Dspring.profiles.active=dev
- (1)、Edit Configurations
-
(2)、設置參數
- (3)、運行查看,激活成功
- 三、命令行參數:java –jar xxx.jar --spring.profiles.active=dev
2.4.3、內部配置加載順序
Springboot程序啟動時,會從以下位置加載配置文件:
- file:./config/:當前項目下的/config目錄下
- file:./ :當前項目的根目錄
- classpath:/config/:classpath的/config目錄
- classpath:/ :classpath的根目錄
加載順序為上文的排列順序,高優先級配置的屬性會生效。
(1)、src路徑下的文件在編譯后會放到WEB-INF/classes路徑下吧。默認的classpath是在這里。用maven構建項目時候resources目錄就是默認的classpath
classes含義:
0、這是一個定位資源的入口
1.存放各種資源配置文件 eg.init.properties log4j.properties struts.xml
2.存放模板文件 eg.actionerror.ftl,靜態資源文件
3.存放class文件 對應的是項目開發時的src目錄編譯文件
classpath 和 classpath* 區別:
classpath:只會到你的class路徑中查找找文件;
classpath*:不僅包含class路徑,還包括jar文件中(class路徑)進行查找。
(2)、項目的java與resources目錄都將打包到classes目錄下:
三、Spring IoC與自動裝配
3.1、Spring框架概述
Spring是一個開源免費的框架,為了解決企業應用開發的復雜性而創建。Spring框架是一個輕量級的解決方案,可以一站式地構建企業級應用。Spring是模塊化的,所以可以只使用其中需要的部分。可以在任何web框架上使用控制反轉(IoC),也可以只使用Hibernate集成代碼或JDBC抽象層。它支持聲明式事務管理、通過RMI或web服務實現遠程訪問,並可以使用多種方式持久化數據。它提供了功能全面的MVC框架,可以透明地集成AOP到軟件中。
Spring被設計為非侵入式的,這意味着你的域邏輯代碼通常不會依賴於框架本身。在集成層(比如數據訪問層),會存在一些依賴同時依賴於數據訪問技術和Spring,但是這些依賴可以很容易地從代碼庫中分離出來。
Spring框架是基於Java平台的,它為開發Java應用提供了全方位的基礎設施支持,並且它很好地處理了這些基礎設施,所以你只需要關注你的應用本身即可。
Spring可以使用POJO(普通的Java對象,plain old java objects)創建應用,並且可以將企業服務非侵入式地應用到POJO。這項功能適用於Java SE編程模型以及全部或部分的Java EE。
那么,做為開發者可以從Spring獲得哪些好處呢?
不用關心事務API就可以執行數據庫事務;
不用關心遠程API就可以使用遠程操作;
不用關心JMX API就可以進行管理操作;
不用關心JMS API就可以進行消息處理。
①JMX,Java Management eXtension,Java管理擴展,是一個為應用程序、設備、系統等植入管理功能的框架。JMX可以跨越一系列異構操作系統平台、系統體系結構和網絡傳輸協議,靈活的開發無縫集成的系統、網絡和服務管理應用。
②JMS,Java Message Service,Java消息服務,是Java平台上有關面向消息中間件(MOM)的技術規范,它便於消息系統中的Java應用程序進行消息交換,並且通過提供標准的產生、發送、接收消息的接口簡化企業應用的開發。
一句話概括:Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器(框架)。
3.2、資源
官網: http://spring.io
文檔: https://docs.spring.io/spring/docs/current/spring-framework-reference/、 https://github.com/waylau/spring-framework-4-reference
中文幫助: http://spring.cndocs.ml/
框架下載地址: http://repo.springsource.org/libs-release-local/org/springframework/spring/
教程: http://www.yiibai.com/spring
Git: https://github.com/spring-projects
源碼: https://github.com/spring-projects/spring-framework
Jar包: https://github.com/spring-projects/spring-framework/releases
3.3、Spring歷史
2002年,Rod Jahnson在《Expert One-on-One J2EE Design and Development》書中首次推出了Spring框架雛形interface21框架。
2004年3月24日,Spring框架以interface21框架為基礎,經過重新設計,發布了1.0正式版。
從2004年3月到現在,已經經歷了1.0、1.1、1.2、2.0、2.5、3.0、3.1幾個主要的版本
3.2.0版發布 2013年5月5日13:53
3.2.10版發布 2014年7月15日23:58
3.2.9版發布 2014年5月20日12:22
4.0.0版發布 2013年12月12日07:50
4.0.1版發布 2014年1月28日20:55
4.1.6版發布 2015年3月25日16:40
4.2.2版發布 2015年10月15日12:57
4.2.5版發布 2016年2月25日09:28
4.3.5版發布 2016年12月21日11:34
4.3.6版發布 2017年1月25日14:05
4.3.8版發布 2017年4月18日13:49
4.3.9版發布 2017年6月7日19:29
5.0.0版發布 2017年9月28日11:28
5.0.1版發布 2017年10月24日15:14
5.3.19版現在
3.4、框架特征與功能
輕量:從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小只有1MB多的JAR文件里發布。並且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。
控制反轉Ioc:Spring通過一種稱作控制反轉(IoC)的技術促進了低耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創建或者查找依賴對象。你可以認為IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。
面向切面Aop:Spring提供了面向切面編程的豐富支持,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該做的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日志或事務支持。
容器:Spring包含並管理應用對象的配置和生命周期,在這個意義上它是一種容器,你可以配置你的每個bean如何被創建——基於一個可配置原型(prototype),你的bean可以創建一個單獨的實例或者每次需要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不應該被混同於傳統的重量級的EJB容器,它們經常是龐大與笨重的,難以使用。
框架:Spring可以將簡單的組件配置、組合成為復雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件里。Spring也提供了很多基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。
MVC:Spring的作用是整合,但不僅僅限於整合,Spring 框架可以被看做是一個企業解決方案級別的框架,Spring MVC是一個非常受歡迎的輕量級Web框架。
所有Spring的這些特征使你能夠編寫更干凈、更可管理、並且更易於測試的代碼。它們也為Spring中的各種模塊提供了基礎支持。
- 方便解耦、簡化開發,Spring 就是一個大工廠,可以將對象創建和依賴關系的維護交給 Spring 管理
- AOP 編程的支持,Spring 提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能
- 聲明式事務的支持,只需要通過配置就可以完成對事務的管理,而無需手動編程
- 方便程序的測試,Spring 對 Junit4 支持,可以通過注解方便的測試 Spring 程序
- 方便集成各種優秀框架,Spring 不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持
- 降低 JavaEEAPI 的使用難度,Spring 對 JavaEE 開發中非常難用的一些 API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些 API 應用難度大大降低
3.5、Spring組成
Spring 框架是一個分層架構,由 7 個定義良好的模塊組成。Spring 模塊構建在核心容器之上,核心容器定義了創建、配置和管理 bean 的方式
組成 Spring 框架的每個模塊(或組件)都可以單獨存在,或者與其他一個或多個模塊聯合實現。每個模塊的功能如下:
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要組件是
BeanFactory
,它是工廠模式的實現。BeanFactory
使用控制反轉(IOC) 模式將應用程序的配置和依賴性規范與實際的應用程序代碼分開。 - Spring 上下文:Spring 上下文是一個配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。
- Spring AOP:通過配置管理特性,Spring AOP 模塊直接將面向方面的編程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何對象支持 AOP。Spring AOP 模塊為基於 Spring 的應用程序中的對象提供了事務管理服務。通過使用 Spring AOP,不用依賴 EJB 組件,就可以將聲明性事務管理集成到應用程序中。
- Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同數據庫供應商拋出的錯誤消息。異常層次結構簡化了錯誤處理,並且極大地降低了需要編寫的異常代碼數量(例如打開和關閉連接)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
- Spring ORM:Spring 框架插入了若干個 ORM 框架,從而提供了 ORM 的對象關系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
- Spring Web 模塊:Web 上下文模塊建立在應用程序上下文模塊之上,為基於 Web 的應用程序提供了上下文。所以,Spring 框架支持與 Jakarta Struts 的集成。Web 模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工作。
- Spring MVC 框架:MVC 框架是一個全功能的構建 Web 應用程序的 MVC 實現。通過策略接口,MVC 框架變成為高度可配置的,MVC 容納了大量視圖技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何 J2EE 服務器中,大多數功能也適用於不受管理的環境。Spring 的核心要點是:支持不綁定到特定 J2EE 服務的可重用業務和數據訪問對象。毫無疑問,這樣的對象可以在不同 J2EE 環境 (Web 或 EJB)、獨立應用程序、測試環境之間重用。
Spring是一個開源的框架,現在的Spring框架構成了一個體系平台,通過Spring的官方網站http://www.springsource.org可以了解到,圍繞着Spring框架本身,還有許多其他優秀的項目:
SpringFramework(Core):核心項目
Spring Web Flow:工作流項目
Spring Security:安全項目
Spring Batch:批量數據處理項目
Spring Android:Android系統支持項目
Spring Social:社交項目
3.6、Spring的功能
Spring Boot 是 Spring 的一套快速配置腳手架,可以基於Spring Boot 快速開發單個微服務,Spring Cloud是一個基於Spring Boot實現的雲應用開發工具;Spring Boot專注於快速、方便集成的單個微服務個體,Spring Cloud關注全局的服務治理框架;Spring Boot使用了約束優於配置的理念,很多集成方案已經幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基於Spring Boot來實現,Spring Boot可以離開Spring Cloud獨立使用開發項目,但是Spring Cloud離不開Spring Boot,屬於依賴的關系。
SpringBoot在SpringClound中起到了承上啟下的作用,如果你要學習SpringCloud必須要學習SpringBoot。
Microservices:微服務是一種開發軟件的架構和組織方法,其中軟件由通過明確定義的 API 進行通信的小型獨立服務組成。這些服務由各個小型獨立團隊負責。微服務架構使應用程序更易於擴展和更快地開發,從而加速創新並縮短新功能的上市時間。
Spring Cloud 是一套完整的微服務解決方案,基於 Spring Boot 框架,准確的說,它不是一個框架,而是一個大的容器,它將市面上較好的微服務框架集成進來,從而簡化了開發
Reactor是Spring提供的非阻塞式響應式編程框架,實現了Reactive Streams規范。 它提供了可組合的異步序列API
Serverless,又叫無服務器。Serverless 強調的是一種架構思想和服務模型,讓開發者無需關心基礎設施(服務器等),而是專注到應用程序業務邏輯上。
3.7、IoC基礎
控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實現IoC的一種方法,也有人認為DI只是IoC的另一種說法。沒有IoC的程序中我們使用面向對象編程對象的創建與對象間的依賴關系完全硬編碼在程序中,對象的創建由程序自己控制,控制反轉后將對象的創建轉移給第三方,個人認為所謂控制反轉就是:獲得依賴對象的方式反轉了。
IoC是Spring框架的核心內容,使用多種方式完美的實現了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置實現IoC。Spring容器在初始化時先讀取配置文件,根據配置文件或元數據創建與組織對象存入容器中,程序使用時再從Ioc容器中取出需要的對象。
采用XML方式配置Bean的時候,Bean的定義信息是和實現分離的,而采用注解的方式可以把兩者合為一體,Bean的定義信息直接以注解的形式定義在實現類中,從而達到了零配置的目的。
控制反轉是一種通過描述(XML或注解)並通過第三方去生產或獲取特定對象的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入(Dependency Injection,DI)。
3.8、Springboot項目中獲取IoC容器的方式
3.8.1、在啟動類中獲取
在Springboot項目中都會存在一個SpringApplication的啟動類,我們通過以下代碼啟動IOC容器。
SpringApplication.run(Application.class, args);
其實run方法會將創建的IOC容器作為返回值返回,那么我們就可以通過聲明一個ApplicationContext對象來接收run方法的返回值。
public class SpringApplication { public static void main(String[] args) { ApplicationContext applicationContext = SpringApplication.run(Application.class, args); Object startSerive = applicationContext.getBean("startSerive"); } }
3.8.2、使用工具類
通過編寫實現了ApplicationContextAware的工具類來獲取IOC容器,當實現了ApplicationContextAware的類在容器中被初始化和加載后,會自動調用ApplicationContextAware中的setApplicationContext方法,將IOC容器傳入setApplicationContext方法的形參中。
以下是用於獲取IOC容器的工具類:
public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; public SpringContextUtil() { } /** * 設置上下文 * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringContextUtil.applicationContext == null) { SpringContextUtil.applicationContext = applicationContext; } } /** * 獲取上下文 * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 通過名字獲取上下文中的bean * @param name * @return */ public static Object getBean(String name){ return applicationContext.getBean(name); } /** * 通過類型獲取上下文中的bean * @param requiredType * @return */ public static Object getBean(Class<?> requiredType){ return applicationContext.getBean(requiredType); } }
上面這個工具類只有在被IOC容器加載完之后才會調用setApplicationContext,那么該怎么把工具類放到IOC容器中呢?我們使用@Import注解來實現,具體使用方法請看下面代碼:
@SpringBootApplication @Import({SpringContextUtil.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
注:不使用@Import也是可以的,例如在SpringContextUtil類上面標注@Component等類似的注解也是可以的。
@Import注解須知:
@Import只能用在類上 ,@Import通過快速導入的方式實現把實例加入spring的IOC容器中
加入IOC容器的方式有很多種,@Import注解可以用於導入第三方包 ,當然@Bean注解也可以,但是@Import注解快速導入的方式更加便捷
3.8、IoC加載
想要讓一個普通類接受 Spring 容器管理,有以下方法
- 使用 @Bean 注解
- 使用 @Controller @Service @Repository @Component 注解標注該類,然后再使用 @ComponentScan 掃描包
- @Import 導入
3.8.1、@Component、@Repository、@Service、@Constroller、@RestConstroller
@Component : 作用在要裝載的Bean上,用於容器掃描標識,加入到IOC容器中。@Repository、@Service、@Constroller、@RestConstroller也衍生自@Component
IBookDAO接口如下:
package com.zhangguo.Spring051.ioc01; /** * 圖書數據訪問接口 */ public interface IBookDAO { /** * 添加圖書 */ public String addBook(String bookname); }
BookDAO實現
package com.zhangguo.Spring051.ioc02; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書數據訪問實現類 */ @Component("bookdaoObj") public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書"+bookname+"成功!"; } }
在類上增加了一個注解Component,在類的開頭使用了@Component注解,它可以被Spring容器識別,啟動Spring后,會自動把它轉成容器管理的Bean。
除了@Component外,Spring提供了3個功能基本和@Component等效的注解,分別對應於用於對DAO,Service,和Controller進行注解。
1:@Repository 用於對DAO實現類進行注解。
2:@Service 用於對業務層注解,但是目前該功能與 @Component 相同。
3:@Constroller用於對控制層注解,但是目前該功能與 @Component 相同。
3.8.2、@ComponentScan
一般來說spring boot默認的掃描路徑是啟動類當前的包和子包。
標注了@Component和@Component的衍生注解如@Controller,@Service,@Repository就可以把當前的Bean加入到IOC容器中。那么SpringBoot是如何知道要去掃描@Component注解的呢。@ComponentScan做的事情就是告訴Spring從哪里找到bean
SpringBoot默認包掃描機制: 從啟動類所在包開始,掃描當前包及其子級包下的所有文件。我們可以通過以下的測試來驗證一下。
@ComponentScan : 常用在啟動類上。
basePackages:標識掃描包路徑(包括子包)下所有類,用於指定包的路徑,進行掃描(默認參數)
excludeFilters={@Filter(classes={A.class,B.class})} :掃描排除
includeFilters={@Filter(classes={C.class,D.class})} : 掃描包含
basePackageClasses: 用於指定某個類的包的路徑進行掃描
includeFilters: 包含的過濾條件
FilterType.ANNOTATION:按照注解過濾
FilterType.ASSIGNABLE_TYPE:按照給定的類型
FilterType.ASPECTJ:使用ASPECTJ表達式
FilterType.REGEX:正則
FilterType.CUSTOM:自定義規則
excludeFilters: 排除的過濾條件,用法和includeFilters一樣
nameGenerator: bean的名稱的生成器
useDefaultFilters: 是否開啟對@Component,@Repository,@Service,@Controller的類進行檢測
@SpringBootApplication @Import(SpringContextUtil.class) @ComponentScan(value = "com.zhangguo",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})}) public class ProfiledemoApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(ProfiledemoApplication.class, args); } }
3.8.3、@Condition條件裝載
@Condition來指定一定條件下注冊組件對象到IoC容器中,所有的條件必須實現Condition接口,重寫matches方法,來決定組件是否注冊。
Spring Boot 包含多個 @Conditional 注釋,可以在@Configuration注解的類和@Bean注解方法中使用。@Conditional類型的注解,可以注解在類上,可以注解在Bean方法上,可以允許基於Spring Environment屬性包含配置,可以僅允許在存在特定資源時包含配置。
(一)、自定義條件注解
application.yml
isAdd: false #是否添加User Bean
IsAddCondition
package com.zhangguo.project1; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 自定義條件加載類 * */ public class IsAddCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); //獲取配置文件 String isAdd=env.getProperty("isAdd"); //獲取屬性值 return isAdd!=null&&isAdd.toLowerCase().equals("true"); //是否為true } }
ConditionConfig: 注解在方法上
package com.zhangguo.project1; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; /**配置類*/ @Configuration public class ConditionConfig { @Bean @Conditional(IsAddCondition.class) public User getUser(){ return new User(); } }
ConditionConfig: 注解在配置類上
package com.zhangguo.iocdemo2.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration @Conditional(IsAddUserCondition.class) public class ConditionConfig { }
測試:
package com.zhangguo.project1; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Conditional; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class Project1Application { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(Project1Application.class, args); User user=run.getBean(User.class); System.out.println(user); } }
(二)、系統定義
逐個打開這 13 個注解,我們發現這些注解上有相同的元注解:
都可以應用在 TYPE 上,也就是說,Spring 自動掃描的一切類 (@Configuration, @Component, @Service, @Repository, or @Controller) 都可以通過添加相應的 @ConditionalOnXxxx 來判斷是否加載
都可以應用在 METHOD 上,所以有 @Bean 標記的方法也可以應用這些注解
都是用了 @Conditional 注解來標記,OnBeanCondition 等自定義 Condition 還是實現了 Condition 接口的。
Spring Boot 包含多個 @Conditional 注釋,可以在@Configuration注解的類和@Bean注解方法中使用。@Conditional類型的注解,可以注解在類上,可以注解在Bean方法上,可以允許基於Spring Environment屬性包含配置,可以僅允許在存在特定資源時包含配置。也可自定義,接下來我們來熟悉一下 Spring Boot 提供的一些具體的條件注解。
1. ConditionalOnProperty
package com.zhangguo.project1; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @ConditionalOnProperty(value = "hello.enabled",havingValue = "true") public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello spring boot!"; } }
這個條件解釋是: application.properties 或 application.yml 文件中 hello.enabled 為 true 才會加載 HelloController 這個 Bean,如果沒有匹配上不會加載,因為 matchIfMissing 默認值是 false。
2. @ConditionalOnBean 和 ConditionalOnMissingBean
有時候我們需要某個 Bean 已經存在應用上下文時才會加載,那么我們會用到 @ConditionalOnBean 注解:
與之相反,有時候我們需要某個 Bean 不存在於應用上下文時才會加載,那么我們會用到 @ConditionalOnMissingBean 注解
package com.zhangguo.project1; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @ConditionalOnBean(User.class) public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello spring boot!"; } }
3.@ConditionalOnClass 和 @ConditionalOnMissingClass
和上面的一樣,只不過判斷某個類是否存在於 classpath 中
4.@ConditionalOnExpression
如果我們有更復雜的多個配置屬性一起判斷,那么我們就可以用這個表達式了:
只有當兩個屬性都為 true 的時候才加載 MyModule。
5.@ConditionalOnSingleCandidate
只有指定類已存在於 BeanFactory 中,並且可以確定單個候選項才會匹配成功
BeanFactory 存在多個 bean 實例,但是有一個 primary 候選項被指定(通常在類上使用 @Primary 注解),也會匹配成功。實質上,如果自動連接具有定義類型的 bean 匹配就會成功
目前,條件只是匹配已經被應用上下文處理的 bean 定義,本身來講,強烈建議僅僅在 auto-configuration 類中使用這個條件,如果候選 bean 被另外一個 auto-configuration 創建,確保使用該條件的要在其后面運行
6.@ConditionalOnResource
如果我們要加載的 bean 依賴指定資源是否存在於 classpath 中,那么我們就可以使用這個注解
看到這個 logback.xml 是不是很親切,在我們引入第三方工具類如 Dozer 等都可以添加類似的開關
7.@ConditionalOnJndi
只有指定的資源通過 JNDI 加載后才加載 bean
8.@ConditionalOnJava
只有運行指定版本的 Java 才會加載 Bean
9.@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication
只有運行在 web 應用里才會加載這個 bean
與之相反,在非 web 環境才加載 bean
10.@ConditionalOnCloudPlatform
這個注解冷的我呼吸都要停止了,只有運行在指定的雲平台上才加載指定的 bean,CloudPlatform 是 org.springframework.boot.cloud 下一個 enum 類型的類,大家可以打開自行看看:
3.8.4、作用域用scope來指定
用來指定bean的作用域
singleton---單例 只創建一個對象。
prototype---原型 想創建多少個就創建多少了。
request---針對Web項目,不同的請求創建單獨的Bean對象,同一個請求共享一個Bean。
session---針對Web項目,不同的會話創建單獨的Bean對象,同一個會話共享一個Bean。
默認情況下IoC容器中的Bean是單例的,多次獲取的是同一個Bean對象:
package com.zhangguo.iocdemo2.demo; import org.springframework.stereotype.Component; @Component public class Student { }
測試是否相等:
package com.zhangguo.iocdemo2.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; import javax.jws.soap.SOAPBinding; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args); Student s1=ctx.getBean(Student.class); Student s2=ctx.getBean(Student.class); System.out.println(s1==s2); } }
結果:
如果想讓Bean是非單例的,可以如下注解:
@Component @Scope("prototype") public class Student { }
測試:
再測試時就為false了,因為兩個Bean不是同一個對象。
3.8.5、@Import 導入普通類到 IoC容器中
Student 類
public class Student { }
配置類
@Configuration @Import(Student.class) public class ImportConfig { }
測試代碼
@Test public void test7() { ApplicationContext ctx = new AnnotationConfigApplicationContext(ImportConfig.class); String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String name : beanDefinitionNames) { System.out.println(name); } }
spring 4.2 以前,該注解只能導入配置類。
3.8.6、Bean的生命周期
@PostConstruct 初始化方法的注解方式 等同與在XML中聲明init-method=init
@PreDestroy 銷毀方法的注解方式 等同於在XML中聲明destory-method=destory

package com.zhangguo.project1; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class BeanLife implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean { @PostConstruct public void init(){ //自定義初始化 } @PreDestroy public void destory(){ //自定義摧毀 } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { } @Override public void setBeanName(String s) { } @Override public void destroy() throws Exception { } @Override public void afterPropertiesSet() throws Exception { } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } }
示例:
BookDAO
package com.zhangguo.iocdemo2.demo; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class BookDAO { public void add(){ System.out.println("添加圖書成功!"); } /**創建該對象時調用*/ @PostConstruct public void init(){ System.out.println("准備創建該對象"); } /**當該對象被回收時調用*/ @PreDestroy public void destory(){ System.out.println("准備銷毀該對象"); } }
測試:
package com.zhangguo.iocdemo2.demo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @SpringBootTest class DemoApplicationTests { @Test public void bookDAOTest(){ ApplicationContext ctx=new AnnotationConfigApplicationContext(DemoApplication.class); BookDAO bean = ctx.getBean(BookDAO.class); bean.add(); } }
結果:
3.8.7、默認名稱
如果使用Compont注解時不指定名稱,基於@Componet及其擴展(如@Servic和自定義等)標注和classpath-scan定義的Bean,注解有一個value屬性,如果提供了,那么就此Bean的名字。如果不提供。就會使用Spring默認的命名機制,即簡單類名且第一個字母小寫。
在類的開頭使用了@Component注解,它可以被Spring識別,啟動Spring后,會自動把它轉成容器管理的Bean。
(1)、根據類型獲取
@Component public class Car { }
測試
@Test public void NameTest(){ ApplicationContext ctx=new AnnotationConfigApplicationContext(DemoApplication.class); Car car1=ctx.getBean(Car.class); //根據類型獲取bean }
(2)、根據默認名稱獲取
@Test public void NameTest(){ ApplicationContext ctx=new AnnotationConfigApplicationContext(DemoApplication.class); Car car2= (Car) ctx.getBean("car"); //根據默認名稱獲取bean }
(3)、根據特定名稱獲取
@Component("byd") public class Car { }
測試
@Test public void NameTest(){ ApplicationContext ctx=new AnnotationConfigApplicationContext(DemoApplication.class); Car car2= (Car) ctx.getBean("byd"); //根據自定義名稱獲取bean System.out.println(car2); }
3.9、自動裝配
從上面示例中可以看出有兩個位置都使用了ApplicationContext初始化容器后獲得需要的Bean,可以通過自動裝配簡化。
3.9.1、@Autowired
@Autowired 可以單獨使用。如果單獨使用,它將按類型裝配。
@Autowired 默認按類型裝配(這個注解是屬於spring的),默認情況下必須要求依賴對象必須存在,如果要允許null 值,可以設置它的required屬性為false,如:@Autowired(required = false) 。
User類
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Component; @Component public class User { }
測試類
package com.zhangguo.autoconfigdemo; import javafx.application.Application; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @SpringBootTest class AutoconfigdemoApplicationTests { @Autowired(required = false) //按類型自動裝配對象,required = true表示必須存在該對象,否則異常,如果false則為null User user; @Autowired ApplicationContext ctx; @Test public void testUser1(){ System.out.println(user); System.out.println(ctx); } }
結果:
3.9.2、@Qualifier
因此,如果在容器中聲明了多個相同類型的bean,則會出現問題,因為 @Autowired 不知道要使用哪個bean來注入。因此,使用 @Qualifier 與 @Autowired 一起,通過指定bean名稱來闡明實際裝配的bean (按姓名連線)。
@Qualifier 默認按名稱裝配(這個注解是屬於spring的),value 默認@Qualifier(value = "")空值。
@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了
IBookDAO接口
package com.zhangguo.autoconfigdemo; /**對圖書訪問的接口*/ public interface IBookDAO { void add(); //添加 }
OracleDAO實現類
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Repository; /**使用Oracle數據庫訪問圖書*/ @Repository("oracle") public class OracleDAO implements IBookDAO { @Override public void add() { System.out.println("Oracle:添加圖書成功!"); } }
MySQLDAO實現類
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Repository; /**使用MySQL數據庫訪問圖書*/ @Repository("mysql") public class MySQLDAO implements IBookDAO { @Override public void add() { System.out.println("MySQL:添加圖書成功!"); } }
BookDAOTest測試類
package com.zhangguo.autoconfigdemo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class BookDAOTest { @Autowired @Qualifier("mysql") /*根據名稱裝配*/ IBookDAO bookDAO; @Test public void bookTest(){ bookDAO.add(); } }
運行結果:
3.9.3、@Resource
@Resource(這個注解屬於J2EE的),默認按照名稱進行裝配,名稱可以通過name屬性進行指定, 如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行按照名稱查找,如果注解寫在setter方法上默認取屬性名進行裝配。 當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
package com.zhangguo.autoconfigdemo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; @SpringBootTest public class BookDAOTest { @Resource(name = "oracle") IBookDAO bookDAO; @Test public void bookTest(){ bookDAO.add(); } }
運行結果:
@Autowired是根據類型進行自動裝配的,如果需要按名稱進行裝配,則需要配合@Qualifier。另外可以使用其它注解,@ Resource :等同於@Qualifier,@Inject:等同於@ Autowired。
@Service用於注解業務層組件(我們通常定義的service層就用這個)
@Controller用於注解控制層組件(如struts中的action)
@Repository用於注解數據訪問組件,即DAO組件
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行注解。
裝配注解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:
1、@Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入;
2、@Autowired默認是按照類型裝配注入的,如果想按照名稱來轉配注入,則需要結合@Qualifier一起使用;
3、@Resource注解是J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven項目是1.5的JRE則需換成更高版本的。
4、@Resource和@Autowired都可以書寫注解在字段或者該字段的setter方法之上
5、@Autowired 可以對成員變量、方法以及構造函數進行注釋,而 @Qualifier 的注解對象是成員變量、方法入參、構造函數入參。
6、@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。
7、@Autowired 注釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設置非必要。
8、@Resource裝配順序
8.1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
8.2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
8.3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
8.4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;
區別:
區別在於 @Autowired 和 @Qualifier 是Spring注解,而 @Resource 是標准的java注解(來自JSR-250)。此外, @Resource 僅支持字段和setter注入,而 @Autowired 支持字段,setter,構造函數和多參數方法注入。
建議使用 @Resource 進行字段和setter注入。堅持使用 @Qualifier 和 @Autowired 進行構造函數或多參數方法注入。
3.9.4、@Primary
@Primary : 優先級裝載標識。如果多個子類裝載,注入的時候優先注入子類上有@Primary標識的。
MySQLDAO
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Repository; /**使用MySQL數據庫訪問圖書*/ @Repository public class MySQLDAO implements IBookDAO { @Override public void add() { System.out.println("MySQL:添加圖書成功!"); } }
OracleDAO
package com.zhangguo.autoconfigdemo; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Repository; /**使用Oracle數據庫訪問圖書*/ @Repository @Primary //優先被裝配 public class OracleDAO implements IBookDAO { @Override public void add() { System.out.println("Oracle:添加圖書成功!"); } }
測試
package com.zhangguo.autoconfigdemo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; @SpringBootTest public class BookDAOTest { @Autowired IBookDAO bookDAO; @Test public void bookTest(){ bookDAO.add(); } }
結果:
3.9.5、@profile
spring profile是Spring Framework 3.1以后推出一個解決切換環境變量的注解。主要解決一個環境問題切換的問題,其原理就是通過spring di在注入的時候通過環境變量來判斷注入相應的環變量,以達到減少配置問題引起的各種麻煩。
IBookDAO
package com.zhangguo.autoconfigdemo; /**對圖書訪問的接口*/ public interface IBookDAO { void add(); //添加 }
MySQLDAO
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Repository; /**使用MySQL數據庫訪問圖書*/ public class MySQLDAO implements IBookDAO { @Override public void add() { System.out.println("MySQL:添加圖書成功!"); } }
OracleDAO
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Repository; /**使用MySQL數據庫訪問圖書*/ public class MySQLDAO implements IBookDAO { @Override public void add() { System.out.println("MySQL:添加圖書成功!"); } }
DbConfig
package com.zhangguo.autoconfigdemo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class DbConfig { @Bean //將該方法返回的實例添加到IoC容器中 @Profile("dev") //如果當前的環境是dev則注入該實例 public IBookDAO getOracle(){ return new OracleDAO(); } @Bean //將該方法返回的實例添加到IoC容器中 @Profile("pro") //如果當前的環境是pro則注入該實例 public IBookDAO getMySQL(){ return new MySQLDAO(); } }
測試類
package com.zhangguo.autoconfigdemo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class ProfileTest { @Autowired IBookDAO bookDAO; @Test public void addBookTest(){ bookDAO.add(); } }
application.yaml
spring:
profiles:
active: pro
結果
3.9.6、多種注入方式
示例:
package spring13; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class InjectTest { //注入給構造方法 @Autowired public InjectTest(IUserDao dao2) { this.dao2=dao2; } //注入給成員變量,字段 @Resource IUserDao dao1; IUserDao dao2; IUserDao dao3; IUserDao dao4; //注入給屬性 @Autowired public void setDao3(IUserDao dao3) { this.dao3 = dao3; } //注入給方法參數 @Autowired public void injectDao4(IUserDao dao4, IUserDao dao5) { this.dao4 = dao4; System.out.println(dao5); } public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean13.xml"); InjectTest obj = ctx.getBean(InjectTest.class); System.out.println(obj.dao1); System.out.println(obj.dao2); System.out.println(obj.dao3); System.out.println(obj.dao4); } } interface IUserDao { } @Scope("prototype") @Repository class UserDao implements IUserDao { }
結果:
3.10、零配置實現IOC
3.10.1、綜合示例
所謂的零配置就是不再使用xml文件來初始化容器,使用一個類型來替代
IBookDAO代碼如下:
package com.zhangguo.Spring051.ioc06; /** * 圖書數據訪問接口 */ public interface IBookDAO { /** * 添加圖書 */ public String addBook(String bookname); }
IBookDAO的實現類BookDAO代碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書數據訪問實現類 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書"+bookname+"成功!"; } }
在BookDAO類上注解了@Repository當初始化時該類將被容器管理會生成一個Bean,可以通過構造方法測試。
業務層BookService代碼如下:
package com.zhangguo.Spring051.ioc06; import javax.annotation.Resource; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { @Resource IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println("圖書上貨"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
類BookService將對容器管理因為注解了@Service,初始化時會生成一個單例的Bean,類型為BookService。在字段bookDAO上注解了@Resource,用於自動裝配,Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入。
新增一個用於替代原xml配置文件的ApplicationCfg類,代碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 容器的配置類 */ @Configuration @ComponentScan(basePackages="com.zhangguo.Spring051.ioc06") public class ApplicationCfg { @Bean public User getUser(){ return new User("成功"); } }
@Configuration相當於配置文件中的<beans/>,ComponentScan相當於配置文件中的context:component-scan,屬性也一樣設置
,@Bean相當於<bean/>,只能注解在方法和注解上,一般在方法上使用,源碼中描述:@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),方法名相當於id。中間使用到了User,User類的代碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; @Component("user1") public class User { public User() { System.out.println("創建User對象"); } public User(String msg) { System.out.println("創建User對象"+msg); } public void show(){ System.out.println("一個學生對象!"); } }
初始化容器的代碼與以前有一些不一樣,具體如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器,注解配置應用程序容器,Spring通過反射ApplicationCfg.class初始化容器 ApplicationContext ctx=new AnnotationConfigApplicationContext(ApplicationCfg.class); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC權威指南 第四版》"); User user1=ctx.getBean("user1",User.class); user1.show(); User getUser=ctx.getBean("getUser",User.class); getUser.show(); } }
容器的初始化通過一個類型完成,Spring通過反射ApplicationCfg.class初始化容器,中間user1與getUser是否為相同的Bean呢?
答案是否定的,因為在ApplicationCfg中聲明的方法getUser當相於在xml文件中定義了一個<bean id="getUser" class="..."/>,在User類上注解@Component("user1")相當於另一個<bean id="user1" class="..."/>。
運行結果:
小結:使用零配置和注解雖然方便,不需要編寫麻煩的xml文件,但並非為了取代xml,應該根據實例需要選擇,或二者結合使用,畢竟使用一個類作為容器的配置信息是硬編碼的,不好在發布后修改。
3.10.2、零配置,由注解指定實例
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於類型的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao1=ctx.getBean(ICarDao.class); dao1.add("Spring Pro"); } } interface ICarDao{ void add(String name); } @Repository class CarDao implements ICarDao{ public void add(String name) { System.out.println("添加"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ }
運行結果:
3.10.3、零配置,由方法指定實例
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於類型的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao2=ctx.getBean("mysqlDao",ICarDao.class); dao2.add("Spring Pro"); } } interface ICarDao{ void add(String name); } class CarDao implements ICarDao{ public void add(String name) { System.out.println("添加"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ @Bean ICarDao mysqlDao(){ //方法名就是bean的name return new CarDao(); } }
運行結果:
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於類型的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao1=ctx.getBean("oracleDao",ICarDao.class); dao1.add("Spring Pro Oracle"); ICarDao dao2=ctx.getBean("mysqlDao",ICarDao.class); dao2.add("Spring Pro MySQL"); System.out.println(dao1==dao2); } } interface ICarDao{ void add(String name); } @Repository("oracleDao") class CarDao implements ICarDao{ public void add(String name) { System.out.println("添加"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ @Bean ICarDao mysqlDao(){ //方法名就是bean的name return new CarDao(); } }
運行結果:
四、作業
1、完成每一個上課示例
2、完成一個我的任務功能,可以記錄自己要完成的任務清單。
實現添加,刪除,變更狀態的功能,要求如下:
(1)、需要用entity、dao、service、controller分層開發,使用IoC。
(2)、數據不需要存在數據庫中,初始數據使用對象數組。
(3)、dao層與controller層每個方法都需要完成單元測試。
(4)、profile:開發模式時我的任務字體為藍色,8881端口,運行模式時我的任務字體為紅色,8882端口。
(5)、 打包,控制台運行,脫離開發環境。
五、視頻
https://www.bilibili.com/video/BV1fi4y1S79P?share_source=copy_web
https://space.bilibili.com/87194917/video
作業解答:https://www.bilibili.com/video/BV1Hs411F71x?share_source=copy_web