1.SpringApplication
1.1 自定义 banner
1.类路径添加banner.txt
或设置spring.banner.location
就可以定制 banner
spring.banner.location=classpath:banner.txt
spring.main.banner-mode=off //关闭
2.推荐网站:Spring Boot banner 在线生成工具,制作下载英文 banner.txt,修改替换 banner.txt 文字实现自定义,个性化启动 banner-bootschool.net
1.2 自定义SpringApplication
默认 :
//SpringApplication:Boot应用的核心API入口
SpringApplication.run(Boot3SsmApplication.class, args);
自定义:
//1.自定义SpringApplication的底层设置
SpringApplication application = new SpringApplication(Boot3SsmApplication.class);
application.setBannerMode(Banner.Mode.OFF);//配置以配置文件优先
//2.SpringApplication运行起来
application.run(args);
1.3 FluentBuilder APl
//2、Builder方式构建 SpringApplication;通过FlventAPI进行设置
new SpringApplicationBuilder()
.main(Boot3SsmApplication.class)
.sources(Boot3SsmApplication.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
2.Profiles
环境隔离能力;快速切换开发、测试、生产环境
步骤:
1.标识环境: 指定哪些组件、配置在哪个环境生效
1)、区分出几个环境: dev(开发环境)、test(测试环境)、prod(生产环境)
2)、指定每个组件在哪个环境下生效; default环境:默认环境
通过: @Profile({“test”})标注
组件没有标注@Profile代表任意时候都生效
3)、默认只有激活指定的环境,这些组件才会生效。
2.切换环境: 这个环境对应的所有组件和配置就应该生效
配置文件激活:spring.profiles.active=test
命令行激活: java jar xxx.jar --spring.profiles.active=test
2.1.使用
2.1.1 指定环境
- Spring Profiles 提供一种隔离配置的方式,使其仅在特定环境生效;
- 任何
@Component
,@Configuration
或@ConfigurationProperties
可以使用@Profile
标记,来指定何时被加载。【容器中的组件都可以被@Profile
标记】
2.1.2 环境激活
1.配置激活指定环境
spring.profiles.active=production
2.也可以使用命令行激活。--spring.profiles.active=dev
3.还可以配置默认环境;不标注@Profile 的组件永远都存在。
以前默认环境叫default 可以修改spring.profiles.default=test
2.1.3 环境包含
注意:
1.spring.profiles.active
和spring.profiles.default
只能用到 无 profile 的文件中,如果在application-dev.yaml
中编写就是无效的(只能写在主配置文件中)
2.也可以额外添加生效文件,而不是激活替换。比如:
spring.profiles.include[e]=common
spring.profiles,include[1l=local
include激不激活都生效
最佳实战:
生效的环境=激活的环境/默认环境 +包含的环境
项目里面这么用
基础的配置 mybatis
、log
、xxx
:写到包含环境中
需要动态切换变化的 db
、redis
:写到激活的环境中
2.2.Profile 分组
创建prod
组,指定包含db
和mq
配置
spring.profiles.group.prod=db,mq
spring.profiles.group.prod[0]=db
spring.profiles.group.prod[1]=mq
使用--spring.profiles.active=prod
,就会激活prod
,db
,mq
配置文件
2.3.Profile 配置文件
application-{profile}.properties
可以作为指定环境的配置文件- 激活这个环境,配置就会生效。最终生效的所有配置是
application.properties
:主配置文件,任意时候都生效。application-{profile}.properties:
指定环境配置文件,激活指定环境生效。
配置文件怎么使用Profile功能
1)、application.properties:主配置文件。任何情况下都生效
2)、其他Profile环境下命名规范:application-{profile标识}.properties:比如:application-dev.properties
3)激活指定环境即可:配置文件激活、命令行激活
4)效果:
项目的所有生效配置项 : 激活环境配置文件的所有项 + 主配置文件和激活文件不冲突的所有项
如果发生了配置冲突,以激活的环境配置文件为准。
application-{profile标识}.properties 优先级 application.properties
3.外部化配置
场景:线上应用和何快速修改配置,并应用最新配置?
- SpringBoot 使用 配置优先级+外部配置 简化配置更新、简化运维。
- 只需要给 jar 应用所在的文件夹放一个 application.properties 最新配置文件,重启项目就能自动应用最新配置。
3.1.配置优先级
Spring Boot 允许将配置外部化,以便可以在不同的环境中使用相同的应用程序代码。
我们可以使用各种外部配置源,包括java Properties文件
、YAML文件
、环境变量
和命令行参数
。@Value
可以获取值,也可以用@ConfigurationProperties
将所有属性绑定到java object
中
以下是 SpringBoot 属性源加载顺序。后面的会覆盖前面的值。
- 默认属性(通过
SpringApplication.setDefaultProperties
指定的) @PropertySource
指定加载的配置(需要写在@Configuration类上才可生效)- 配置文件(
application.properties/yml
等) RandomValuePropertySource
支持的random.*
配置(如:@Value("${random.int}”))- OS 环境变量
- Java 系统属性(
System.getProperties()
) - JNDI属性(来自
java:comp/env
) ServletContext
初始化参数ServletConfig
初始化参数SPRING APPLICATION JSON
属性(内置在环境变量或系统属性中的JSON)- 命令行参数
- 测试属性。(
@SpringBootTest
进行测试时指定的属性) - 测试类
@TestPropertySource
注解 - Devtools 设置的全局属性。(
$HOME/.config/spring-boot
)
结论:配置可以写到很多位置,常见的优先级顺序:命令行
>配置文件
>springapplication配置
配置文件优先级(后面覆盖前面)
- jar 包内的application.properties/yml
- jar 包内的application-{profile}.properties/yml
- jar 包外的application.properties/yml
- jar 包外的application-{profile).properties/yml
建议:用一种格式的配置文件。如果.properties和.yml同时存在,则.properties优先
所有参数均可由命令行传入,使用 --参数项=参数值
,将会被添加到环境变量中,并优先于 配置文件比如 java -jar app.jar --name=“spring”,可以使用 @Value("${name}")
获取
3.2.外部配置
SpringBoot 应用启动时会自动寻找application.properties和application.yaml位置,进行加载。顺序如(后面覆盖前面)下:
- 类路径
a.类根路径
b.类下/config
包 - 当前路径(项目所在的位置)
a.当前路径
b.当前下/config
子目录
c./config
日录的直接子目录
导入配置优先级低于配置文件的优先级
3.4.属性占位符
配置文件中可以使用 ${name:default}
形式取出之前配置过的值。
代码中可以使用@Value("${name:default}")
аpp.паme=MуAрр
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
4.单元测试-JUnit5
4.1.整合
SpringBoot 提供一系列测试工具集及注解方便我们进行测试。
spring-boot-test
提供核心测试能力,spring-boot-test-autoconfigure
提供测试的一些自动配置。我们只需要导入spring-boot-starter-test
即可整合测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
spring-boot-starter-test
默认提供了以下库供我们测试使用
- JUnit 5
- Spring Test
- Assert
- Hamcrest
- Mockito
- JSONassert
- JsonPath
加粗样式
4.2.测试
4.2.0 组件测试
直接 @Autowired
容器中的组件进行测试
4.2.1 注解
JUnit5的注解与JUnit4的注解有所变化
JUnit 5 User Guide
- @Test :表示方法是测试方法。但是与Jnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍@RepeatedTest :表示方法可重复执行,下方会有详细介绍@DisplayName :为测试类或者测试方法设置展示名称
- @BeforeEach:表示在每个单元测试之前执行
- @AfterEach :表示在每个单元测试之后执行@BeforeAll :表示在所有单元测试之前执行
- @AfterAll :表示在所有单元测试之后执行
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories
- @Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout:表示测试方法运行如果超过了指定时间将会返回错误
- @Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout:表示测试方法运行如果超过了指定时间将会返回错误
- @ExtendWith :为测试类或测试方法提供扩展类引用
4.2.2断言 Assertions
- assertEquals :判断两个对象或两个原始类型是否相等
- assertNotEquals :判断两个对象或两个原始类型是否不相等
- assertSame :判断两个对象引用是否指向同一个对象
- assertNotSame :判断两个对象引用是否指向不同的对象
- assertTrue :判断给定的布尔值是否为 true
- assertFalse :判断给定的布尔值是否为 false
- assertNull :判断给定的对象引用是否为 null
- assertNotNull :判断给定的对象引用是否不为 null
- assertArrayEquals :数组断言
- assertAll :组合断言
- assertThrows :异常断言
- assertTimeout :超时断言
- fail :快速失败
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.concurrent.CountDownLatch;
import example.domain.Person;
import example.util.Calculator;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class AssertionsDemo {
private final Calculator calculator = new Calculator();
private final Person person = new Person("Jane", "Doe");
@Test
void standardAssertions() {
assertEquals(2, calculator.add(1, 1));
assertEquals(4, calculator.multiply(2, 2),
"The optional failure message is now the last parameter");
assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
+ "to avoid constructing complex messages unnecessarily.");
}
@Test
void groupedAssertions() {
// In a grouped assertion all assertions are executed, and all
// failures will be reported together.
assertAll("person",
() -> assertEquals("Jane", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}
@Test
void dependentAssertions() {
// Within a code block, if an assertion fails the
// subsequent code in the same block will be skipped.
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);
// Executed only if the previous assertion is valid.
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("e"))
);
},
() -> {
// Grouped assertion, so processed independently
// of results of first name assertions.
String lastName = person.getLastName();
assertNotNull(lastName);
// Executed only if the previous assertion is valid.
assertAll("last name",
() -> assertTrue(lastName.startsWith("D")),
() -> assertTrue(lastName.endsWith("e"))
);
}
);
}
@Test
void exceptionTesting() {
Exception exception = assertThrows(ArithmeticException.class, () ->
calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
}
@Test
void timeoutNotExceeded() {
// The following assertion succeeds.
assertTimeout(ofMinutes(2), () -> {
// Perform task that takes less than 2 minutes.
});
}
@Test
void timeoutNotExceededWithResult() {
// The following assertion succeeds, and returns the supplied object.
String actualResult = assertTimeout(ofMinutes(2), () -> {
return "a result";
});
assertEquals("a result", actualResult);
}
@Test
void timeoutNotExceededWithMethod() {
// The following assertion invokes a method reference and returns an object.
String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
assertEquals("Hello, World!", actualGreeting);
}
@Test
void timeoutExceeded() {
// The following assertion fails with an error message similar to:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}
@Test
void timeoutExceededWithPreemptiveTermination() {
// The following assertion fails with an error message similar to:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
new CountDownLatch(1).await();
});
}
private static String greeting() {
return "Hello, World!";
}
}
4.2.3 嵌套测试
JUnit 5 可以通过 Javà 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.EmptyStackException;
import java.util.Stack;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class HelloTest {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new stack()")
void isInstantiatedWithNew(){
new Stack<>();
}
@Nested
@DisplayName("when new ")
class whenNew{
@BeforeEach
void createNewStack(){
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty(){
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped(){
assertThrows(EmptyStackException.class,stack::pop);
}
@Test
@DisplayName("throws EmptystackException when peeked")
void throwsExceptionWhenPeeked(){
assertThrows(EmptyStackException.class,stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing{
String anElement ="an element";
@BeforeEach
void pushAnElement(){
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty(){
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
}
}
}
}
4.2.4 参数化测试
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用**@ValueSource**等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource:为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型**@NullSource**: 表示为参数化测试提供一个nul的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
@ParameterizedTest //参数化测试
@ValueSource(strings ={"one","two","three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(string string){
System.out.printin(string);
Assertions.assertTrue(stringUtils.isNotBlank(string));
}
@ParameterizedTest//指定方法创
@MethodSource("method")//指定方法名,返回值就是测试用的参数
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(string name){
System.out.println(name);
Assertions.assertNotNull(name);
}
//返回stream即可
static stream<String> method(){
return Stream.of("apple","banana");
}