WEB场景 Developing Web Applications
Spring Boot为Spring MVC提供了自动配置功能,对大多数应用程序都很适用。自动配置在Spring的默认基础上增加了以下功能:
功能
说明
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
内容协商视图解析器和BeanName视图解析器
Support for serving static resources, including support for WebJars
静态资源(包括webjars)
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
自动注册 Converter,GenericConverter,Formatter
Support for HttpMessageConverters
支持 HttpMessageConverters
Automatic registration of MessageCodesResolver
自动注册 MessageCodesResolver
(国际化用)
Static index.html
support.
静态index.html 页支持
Custom Favicon
support
自定义 Favicon
Automatic use of a ConfigurableWebBindingInitializer
bean
自动使用 ConfigurableWebBindingInitializer
不用@EnableWebMvc注解。使用 @Configuration
+ WebMvcConfigurer
自定义规则
声明 WebMvcRegistrations
改变默认底层组件
使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
静态资源规则与定制化
静态资源目录
只要静态资源放在类路径下: called /static
(or /public
or /resources
or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理 : 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
改变默认的静态资源路径
1 2 3 4 5 6 7 spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/ ]
静态资源访问前缀
默认无前缀
1 2 3 spring: mvc: static-path-pattern: /res/**
当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找
webjar
可用jar方式添加css,js等资源文件
例如,添加jquery
1 2 3 4 5 <dependency > <groupId > org.webjars</groupId > <artifactId > jquery</artifactId > <version > 3.5.1</version > </dependency >
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径。
Welcome 与 Favicon
欢迎页支持
静态资源路径下 index.html
可以配置静态资源路径
不可以配置静态资源的访问前缀 。否则导致 index.html不能被默认访问
1 2 3 4 5 spring: resources: static-locations: [classpath:/haha/ ]
controller能处理/index
自定义Favicon
favicon.ico 放在静态资源目录下即可。
静态资源原理
SpringBoot启动默认加载 xxxAutoConfiguration
类(自动配置类)
SpringMVC功能的自动配置类 WebMvcAutoConfiguration
,生效
1 2 3 4 5 6 7 8 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {}
给文件中配置的内容
1 2 3 4 5 6 7 @Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
配置文件的相关属性的绑定:WebMvcProperties 绑定 spring.mvc
1 2 @ConfigurationProperties(prefix = "spring.mvc") public class WebMvcProperties {
配置文件的相关属性的绑定:ResourceProperties 绑定spring.resources
1 2 @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties {
配置类只有一个有参构造器
有参构造器所有参数的值都会从容器中确定
1 2 3 4 5 6 7 8 9 10 11 12 13 public WebMvcAutoConfigurationAdapter (ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) { this .resourceProperties = resourceProperties; this .mvcProperties = mvcProperties; this .beanFactory = beanFactory; this .messageConvertersProvider = messageConvertersProvider; this .resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this .dispatcherServletPath = dispatcherServletPath; this .servletRegistrations = servletRegistrations; }
参数
说明
ResourceProperties resourceProperties
获取和spring.resources绑定的所有的值的对象
WebMvcProperties mvcProperties
获取和spring.mvc绑定的所有的值的对象
ListableBeanFactory beanFactory
Spring的beanFactory
HttpMessageConverters
找到所有的HttpMessageConverters
ResourceHandlerRegistrationCustomizer
找到 资源处理器的自定义器。
DispatcherServletPath
ServletRegistrationBean
给应用注册Servlet、Filter….
资源处理的默认规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public void addResourceHandlers (ResourceHandlerRegistry registry) { if (!this .resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled" ); return ; } Duration cachePeriod = this .resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this .resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); if (!registry.hasMappingForPattern("/webjars/**" )) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**" ) .addResourceLocations("classpath:/META-INF/resources/webjars/" ) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } String staticPathPattern = this .mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this .resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
配置禁止所有静态资源规则
1 2 3 spring: resources: add-mappings: false
静态资源规则
1 2 3 4 5 6 String staticPathPattern = this .mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this .resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/" , "classpath:/resources/" , "classpath:/static/" , "classpath:/public/" }; private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; ... }
欢迎页处理规则
1 2 3 4 5 6 7 8 9 10 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping (ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping ( new TemplateAvailabilityProviders (applicationContext), applicationContext, getWelcomePage(), this .mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations()); return welcomePageHandlerMapping; }
1 2 3 4 5 6 7 8 9 10 11 12 WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) { if (welcomePage.isPresent() && "/**" .equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage.get()); setRootViewName("forward:index.html" ); } else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { logger.info("Adding welcome page template: index" ); setRootViewName("index" ); } }
模板引擎 thymeleaf 基本使用
依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
springboot自动配置
1 2 3 4 5 6 7 @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { ... }
配置策略
所有thymeleaf的配置值都在 ThymeleafProperties
配置好了 SpringTemplateEngine
配好了 ThymeleafViewResolver
只需要直接开发页面
1 2 3 4 5 6 7 8 9 10 11 12 @ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "classpath:/templates/" ; public static final String DEFAULT_SUFFIX = ".html" ; ... }
thymeleaf 基础语法
表达式
表达式名字
语法
用途
变量取值
${…}
获取请求域、session域、对象等值
选择变量
*{…}
获取上下文对象值
消息
#{…}
获取国际化等值
链接
@{…}
生成链接
片段表达式
~{…}
jsp:include 作用,引入公共页面片段
字面量
文本值: ‘one text’ , ‘Another one!’ ,…
数字: 0 , 34 , 3.0 , 12.3 ,…
布尔值: true , false
空值: null
变量: one,two,…. 变量不能有空格
文本操作
字符串拼接: +
变量替换: |The name is ${name}|
数学运算
布尔运算
运算符: and , or
一元运算: ! , not
比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )
等式: == , != ( eq , ne )
条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
特殊操作
设置属性值
设置单个值
1 2 3 4 5 6 <form action ="subscribe.html" th:attr ="action=@{/subscribe}" > <fieldset > <input type ="text" name ="email" /> <input type ="submit" value ="Subscribe!" th:attr ="value=#{subscribe.submit}" /> </fieldset > </form >
设置多个值
1 2 <img src ="../../images/gtvglogo.png" th:attr ="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
官方文档 - 5 Setting Attribute Values
迭代 1 2 3 4 5 <tr th:each ="prod : ${prods}" > <td th:text ="${prod.name}" > Onions</td > <td th:text ="${prod.price}" > 2.41</td > <td th:text ="${prod.inStock}? #{true} : #{false}" > yes</td > </tr >
1 2 3 4 5 <tr th:each ="prod,iterStat : ${prods}" th:class ="${iterStat.odd}? 'odd'" > <td th:text ="${prod.name}" > Onions</td > <td th:text ="${prod.price}" > 2.41</td > <td th:text ="${prod.inStock}? #{true} : #{false}" > yes</td > </tr >
条件运算 1 2 3 <a href ="comments.html" th:href ="@{/product/comments(prodId=${prod.id})}" th:if ="${not #lists.isEmpty(prod.comments)}" > view</a >
1 2 3 4 5 <div th:switch ="${user.role}" > <p th:case ="'admin'" > User is an administrator</p > <p th:case ="#{roles.manager}" > User is a manager</p > <p th:case ="*" > User is some other thing</p > </div >
属性优先级
Order
Feature
Attributes
1
Fragment inclusion
th:insert
th:replace
2
Fragment iteration
th:each
3
Conditional evaluation
th:if
th:unless
th:switch
th:case
4
Local variable definition
th:object
th:with
5
General attribute modification
th:attr
th:attrprepend
th:attrappend
6
Specific attribute modification
th:value
th:href
th:src
...
7
Text (tag body modification)
th:text
th:utext
8
Fragment specification
th:fragment
9
Fragment removal
th:remove
官方文档 - 10 Attribute Precedence
数据访问 数据库场景的自动配置 官方文档
maven仓库
导入jdbc场景依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jdbc</artifactId > </dependency >
相关数据源配置类
修改配置项
1 2 3 4 5 6 spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver
整合数据源 官方文档 - Druid连接池介绍
Spring Boot整合第三方技术的两种方式:
自定义方式
导入依赖
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.17</version > </dependency >
配置数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class MyDataSourceConfig { @ConfigurationProperties("spring.datasource") @Bean public DataSource dataSource () throws SQLException { DruidDataSource druidDataSource = new DruidDataSource (); druidDataSource.setFilters("stat,wall" ); return druidDataSource; } }
更多配置项
配置Druid的监控页功能
starter 整合方式 官方文档 - Druid Spring Boot Starter
引入依赖
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.17</version > </dependency >
自动配置
扩展配置项 spring.datasource.druid
自动配置类DruidDataSourceAutoConfigure
DruidSpringAopConfiguration.class
, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
DruidStatViewServletConfiguration.class
, 监控页的配置。spring.datasource.druid.stat-view-servlet
默认开启。
DruidWebStatFilterConfiguration.class
,web监控配置。spring.datasource.druid.web-stat-filter
默认开启。
DruidFilterConfiguration.class
所有Druid的filter的配置:
1 2 3 4 5 6 7 8 private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat" ;private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config" ;private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding" ;private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j" ;private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j" ;private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2" ;private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log" ;private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall" ;
配置实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: root driver-class-name: com.mysql.jdbc.Driver druid: aop-patterns: com.cyan.* filters: stat,wall stat-view-servlet: enabled: true login-password: admin login-username: admin reset-enable: false web-stat-filter: enabled: true url-pattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' filter: stat: slow-sql-millis: 1000 log-slow-sql: true enabled: true wall: enabled: true
整合MyBatis
配置模式:
1 2 3 4 5 6 7 8 9 10 @EnableConfigurationProperties(MybatisProperties.class) : MyBatis配置项绑定类。@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration { ... } @ConfigurationProperties(prefix = "mybatis") public class MybatisProperties { ... }
1 2 3 4 5 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.1.4</version > </dependency >
全局配置文件mybatis-config.xml
(选建)
SpringBoot中可以直接在application.yaml
定义mybatis配置规则,不需要建立配置文件
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings > </configuration >
1 2 3 4 5 6 7 8 9 mybatis: mapper-locations: classpath:mybatis/mapper/*.xml configuration: map-underscore-to-camel-case: true
配置private Configuration configuration;
也就是配置mybatis.configuration
相关的,就是相当于改mybatis全局配置文件中的值。(也就是说配置了mybatis.configuration
,就不需配置mybatis全局配置文件了)
总结
导入MyBatis官方Starter。
编写Mapper接口,需@Mapper
注解。
编写SQL映射文件并绑定Mapper接口。
在application.yaml
中指定Mapper配置文件的所处位置,以及指定全局配置文件的信息 (建议:配置在mybatis.configuration
)
整合MP IDEA的MyBatis的插件 - MyBatisX
MyBatisPlus官网
MyBatisPlus官方文档
MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MP笔记
自动配置
MybatisPlusAutoConfiguration
配置类,MybatisPlusProperties
配置项绑定。
SqlSessionFactory
自动配置好,底层是容器中默认的数据源。
mapperLocations
自动配置好的,有默认值classpath*:/mapper/**/*.xml
,这表示任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件放在 mapper下。
容器中也自动配置好了SqlSessionTemplate
。
@Mapper
标注的接口也会被自动扫描,建议直接 @MapperScan("com.lun.boot.mapper")
批量扫描。
MyBatisPlus优点 之一:只需要我们的Mapper继承MyBatisPlus的BaseMapper
就可以拥有CRUD能力,减轻开发工作。
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.1</version > </dependency >
1 public interface UserMapper extends BaseMapper <User>{}
单元测试 基本介绍 Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
JUnit 5官方文档
作为最新版本的JUnit框架,JUnit5与之前版本的JUnit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
名称
说明
JUnit Platform
是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter
提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部包含了一个测试引擎 ,用于在Junit Platform上运行。
JUnit Vintage
提供了兼容JUnit4.x,JUnit3.x的测试引擎。
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容JUnit4需要自行引入(不能使用JUnit4的功能 @Test)
JUnit 5’s Vintage已经从spring-boot-starter-test
从移除。如果需要继续兼容Junit4需要自行引入Vintage依赖:
常用注解 官方文档 - Annotations
注解
说明
@Test
表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性 ,拓展的测试将会由Jupiter提供额外测试
@ParameterizedTest
表示方法是参数化测试。
@RepeatedTest
表示方法可重复执行。
@DisplayName
为测试类或者测试方法设置展示名称。
@BeforeEach
表示在每个 单元测试之前 执行。
@AfterEach
表示在每个 单元测试之后 执行。
@BeforeAll
表示在所有 单元测试之前 执行。
@AfterAll
表示在所有 单元测试之后 执行。
@Tag
表示单元测试类别,类似于JUnit4中的@Categories。
@Disabled
表示测试类或测试方法不执行,类似于JUnit4中的@Ignore。
@Timeout
表示测试方法运行如果超过了指定时间将会返回错误。
@ExtendWith
为测试类或测试方法提供扩展类引用。
断言机制 断言Assertion是测试方法中的核心部分,用来对测试需要满足的条件进行验证 。这些断言方法都是org.junit.jupiter.api.Assertions的静态方法。检查业务逻辑返回的数据是否合理。所有的测试运行结束以后,会有一个详细的测试报告。
简单断言 用来对单个值进行简单的验证。如:
方法
说明
assertEquals
判断两个对象或两个原始类型是否相等
assertNotEquals
判断两个对象或两个原始类型是否不相等
assertSame
判断两个对象引用是否指向同一个对象
assertNotSame
判断两个对象引用是否指向不同的对象
assertTrue
判断给定的布尔值是否为 true
assertFalse
判断给定的布尔值是否为 false
assertNull
判断给定的对象引用是否为 null
assertNotNull
判断给定的对象引用是否不为 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test @DisplayName("simple assertion") public void simple () { assertEquals(3 , 1 + 2 , "simple math" ); assertNotEquals(3 , 1 + 1 ); assertNotSame(new Object (), new Object ()); Object obj = new Object (); assertSame(obj, obj); assertFalse(1 > 2 ); assertTrue(1 < 2 ); assertNull(null ); assertNotNull(new Object ()); }
数组断言 通过assertArrayEquals
方法来判断两个对象或原始类型的数组是否相等。
1 2 3 4 5 @Test @DisplayName("array assertion") public void array () { assertArrayEquals(new int []{1 , 2 }, new int [] {1 , 2 }); }
组合断言 assertAll()
方法接受多个 org.junit.jupiter.api.Executable
函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言。
1 2 3 4 5 6 7 8 @Test @DisplayName("assert all") public void all () { assertAll("Math" , () -> assertEquals(2 , 1 + 1 ), () -> assertTrue(1 > 0 ) ); }
异常断言 在JUnit4时期,想要测试方法的异常情况时,需要用@Rule
注解的ExpectedException
变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows()
,配合函数式编程就可以进行使用。
1 2 3 4 5 6 7 @Test @DisplayName("异常测试") public void exceptionTest () { ArithmeticException exception = Assertions.assertThrows( ArithmeticException.class, () -> System.out.println(1 % 0 )); }
超时断言 JUnit5还提供了Assertions.assertTimeout()
为测试方法设置了超时时间。
1 2 3 4 5 6 @Test @DisplayName("超时测试") public void timeoutTest () { Assertions.assertTimeout(Duration.ofMillis(1000 ), () -> Thread.sleep(500 )); }
快速失败 通过 fail 方法直接使得测试失败。
1 2 3 4 5 @Test @DisplayName("fail") public void shouldFail () { fail("This should fail" ); }
前置条件 Unit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言assertions 会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止 。
前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @DisplayName("前置条件") public class AssumptionsTest { private final String environment = "DEV" ; @Test @DisplayName("simple") public void simpleAssume () { assumeTrue(Objects.equals(this .environment, "DEV" )); assumeFalse(() -> Objects.equals(this .environment, "PROD" )); } @Test @DisplayName("assume then do") public void assumeThenDo () { assumingThat( Objects.equals(this .environment, "DEV" ), () -> System.out.println("In DEV" ) ); } }
assumeTrue
和 assumFalse
确保给定的条件为 true
或 false
,不满足条件会使得测试执行终止。
assumingThat
的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable
对象才会被执行;当条件不满足时,测试执行并不会终止。
嵌套测试 官方文档 - Nested Tests
JUnit 5 可以通过 Java 中的内部类和@Nested
注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach
和@AfterEach
注解,而且嵌套的层次没有限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 @DisplayName("A stack") class TestingAStackDemo { 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()); assertFalse(stack.isEmpty()); } } } }
参数化测试 官方文档 - Parameterized Tests
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
注解
说明
@ValueSource
为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource
表示为参数化测试提供一个null的入参
@EnumSource
表示为参数化测试提供一个枚举入参
@CsvFileSource
表示读取指定CSV文件内容作为参数化测试入参
@MethodSource
表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider
接口,任何外部文件都可以作为它的入参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @ParameterizedTest @ValueSource(strings = {"one", "two", "three"}) @DisplayName("参数化测试1") public void parameterizedTest1 (String string) { System.out.println(string); Assertions.assertTrue(StringUtils.isNotBlank(string)); } @ParameterizedTest @MethodSource("method") @DisplayName("方法来源参数") public void testWithExplicitLocalMethodSource (String name) { System.out.println(name); Assertions.assertNotNull(name); } static Stream<String> method () { return Stream.of("apple" , "banana" ); }
指标监控 SpringBoot Actuator 未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
官方文档 - Spring Boot Actuator: Production-ready Features
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency >
访问http://localhost:8080/actuator/**
。
暴露所有监控信息为HTTP。
1 2 3 4 5 6 management: endpoints: enabled-by-default: true web: exposure: include: '*'
Endpoint
最常用的Endpoint:
Health:监控状况
Metrics:运行时指标
Loggers:日志记录
Health Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告。
很多的健康检查默认已经自动配置好了,比如:数据库、redis等。
可以很容易的添加自定义的健康检查机制。
Metrics Endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到:
通过Metrics对接多种监控系统。
简化核心Metrics开发。
添加自定义Metrics或者扩展已有Metrics。
开启与禁用Endpoints
默认所有的Endpoint除过shutdown都是开启的。
需要开启或者禁用某个Endpoint。配置模式为management.endpoint.<endpointName>.enabled = true
1 2 3 4 management: endpoint: beans: enabled: true
或者禁用所有的Endpoint然后手动开启指定的Endpoint。
1 2 3 4 5 6 7 8 management: endpoints: enabled-by-default: false endpoint: beans: enabled: true health: enabled: true