springboot3--基础特性

本文介绍了如何自定义SpringApplication的启动banner,使用Profiles进行环境隔离配置,以及SpringBoot的配置优先级和外部化配置。此外,还详细讲解了如何在SpringBoot项目中集成JUnit5进行单元测试,包括注解、断言和参数化测试的使用。
摘要由CSDN通过智能技术生成

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激不激活都生效

最佳实战:
生效的环境=激活的环境/默认环境 +包含的环境
项目里面这么用
基础的配置 mybatislogxxx:写到包含环境中
需要动态切换变化的 dbredis:写到激活的环境中

2.2.Profile 分组

创建prod组,指定包含dbmq配置

spring.profiles.group.prod=db,mq

spring.profiles.group.prod[0]=db
spring.profiles.group.prod[1]=mq

使用--spring.profiles.active=prod,就会激活proddbmq配置文件

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 属性源加载顺序。后面的会覆盖前面的值。

  1. 默认属性(通过SpringApplication.setDefaultProperties指定的)
  2. @PropertySource指定加载的配置(需要写在@Configuration类上才可生效)
  3. 配置文件(application.properties/yml等)
  4. RandomValuePropertySource支持的random.*配置(如:@Value("${random.int}”))
  5. OS 环境变量
  6. Java 系统属性(System.getProperties())
  7. JNDI属性(来自java:comp/env)
  8. ServletContext 初始化参数
  9. ServletConfig 初始化参数
  10. SPRING APPLICATION JSON属性(内置在环境变量或系统属性中的JSON)
  11. 命令行参数
  12. 测试属性。(@SpringBootTest进行测试时指定的属性)
  13. 测试类@TestPropertySource注解
  14. Devtools 设置的全局属性。($HOME/.config/spring-boot)
    结论:配置可以写到很多位置,常见的优先级顺序:命令行 > 配置文件 >springapplication配置

配置文件优先级(后面覆盖前面)

  1. jar 包内的application.properties/yml
  2. jar 包内的application-{profile}.properties/yml
  3. jar 包外的application.properties/yml
  4. jar 包外的application-{profile).properties/yml

建议:用一种格式的配置文件。如果.properties和.yml同时存在,则.properties优先

所有参数均可由命令行传入,使用 --参数项=参数值 ,将会被添加到环境变量中,并优先于 配置文件比如 java -jar app.jar --name=“spring”,可以使用 @Value("${name}")获取

3.2.外部配置

SpringBoot 应用启动时会自动寻找application.properties和application.yaml位置,进行加载。顺序如(后面覆盖前面)下:

  1. 类路径
    a.类根路径
    b.类下/config
  2. 当前路径(项目所在的位置)
    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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值