数据访问
1、SQL
1.1、数据源的自动配置-HiKariDataSource
1.1.1、导入JDBC场景
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
数据库驱动?
为什么导入JDBC场景,官方不导入驱动?官方不知道我们接下要操作什么数据库。
数据库版本和驱动版本对应
默认版本:<mysql.version>8.0.22</mysql.version><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.49</version>--></dependency>
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)<properties><java.version>1.8</java.version><mysql.version>5.1.49</mysql.version></properties>
1.1.2、分析自动配置
自动配置的类:
- DataSourceAutoConfiguration : 数据源的自动配置
- 修改数据源相关的配置:spring.datasource
- 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
- 底层配置好的连接池是:HikariDataSource
@Configuration(proxyBeanMethods = false)@Conditional(PooledDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration
- DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置
- JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud
- 可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
- @Bean@Primary JdbcTemplate;容器中有这个组件
- JndiDataSourceAutoConfiguration: jndi的自动配置
- XADataSourceAutoConfiguration: 分布式事务相关的
1.1.3、修改配置项
spring:datasource:url: jdbc:mysql://localhost:3306/db_accountusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver
1.1.4、测试
@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {@AutowiredJdbcTemplate jdbcTemplate;@Testvoid contextLoads() {// jdbcTemplate.queryForObject("select * from account_tbl")
// jdbcTemplate.queryForList("select * from account_tbl",)Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);log.info("记录总数:{}",aLong);}}
1.1.2、使用Druid数据源
- druid官方github地址
https://github.com/alibaba/druid
整合第三方技术的两种方式:
- 自定义
- 使用官方starter方式
-
自定义方式
2.1 创建数据源<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.17</version></dependency><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"destroy-method="close"><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /><property name="maxActive" value="20" /><property name="initialSize" value="1" /><property name="maxWait" value="60000" /><property name="minIdle" value="1" /><property name="timeBetweenEvictionRunsMillis" value="60000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="testWhileIdle" value="true" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="poolPreparedStatements" value="true" /><property name="maxOpenPreparedStatements" value="20" />
2.2 StatViewServlet
StatViewServlet的用途包括:- 提供监控信息展示的html页面
- 提供监控信息的JSON API
<servlet><servlet-name>DruidStatView</servlet-name><servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class> </servlet> <servlet-mapping><servlet-name>DruidStatView</servlet-name><url-pattern>/druid/*</url-pattern> </servlet-mapping>
2.3. StatFilter
用于统计监控信息;如SQL监控、URI监控需要给数据源中配置如下属性;可以允许多个filter,多个用,分割;如:<property name="filters" value="stat,slf4j" />
系统中所有filter:
别名 Filter类名 default com.alibaba.druid.filter.stat.StatFilter stat com.alibaba.druid.filter.stat.StatFilter mergeStat com.alibaba.druid.filter.stat.MergeStatFilter encoding com.alibaba.druid.filter.encoding.EncodingConvertFilter log4j com.alibaba.druid.filter.logging.Log4jFilter log4j2 com.alibaba.druid.filter.logging.Log4j2Filter slf4j com.alibaba.druid.filter.logging.Slf4jLogFilter commonlogging com.alibaba.druid.filter.logging.CommonsLogFilter 慢SQL记录配置:
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"><property name="slowSqlMillis" value="10000" /><property name="logSlowSql" value="true" /> </bean> 使用 slowSqlMillis 定义慢SQL的时长
-
使用官方starter方式
3.1 引入druid-starter<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version> </dependency>
3.2 分析自动配置
● 扩展配置项 spring.datasource.druid
● 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的配置
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";
3.3 配置示例
spring:datasource:url: jdbc:mysql://localhost:3306/db_accountusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverdruid:aop-patterns: com.atguigu.admin.* #监控SpringBeanfilters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙)stat-view-servlet: # 配置监控页功能enabled: truelogin-username: adminlogin-password: adminresetEnable: falseweb-stat-filter: # 监控webenabled: trueurlPattern: /*exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'filter:stat: # 对上面filters里面的stat的详细配置slow-sql-millis: 1000logSlowSql: trueenabled: truewall:enabled: trueconfig:drop-table-allow: false
SpringBoot配置示例:
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
配置项列表:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
1.1.3、整合Mybatis操作
https://github.com/mybatis
starter
SpringBoot官方的Starter命名方式:spring-boot-starter-xxx
第三方的Starter命名方式: xxx-spring-boot-starter
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version>
</dependency>
-
配置模式
● 全局配置文件
● SqlSessionFactory: 自动配置好了
● SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
● @Import(AutoConfiguredMapperScannerRegistrar.class);
● Mapper: 只要我们写的操作MyBatis的接口标准了 @Mapper 就会被自动扫描进来@EnableConfigurationProperties(MybatisProperties.class) : MyBatis配置项绑定类。 @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration{} @ConfigurationProperties(prefix = "mybatis") public class MybatisProperties
可以修改配置文件中 mybatis 开始的所有;
# 配置mybatis规则 mybatis:config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置 Mapper接口--->绑定Xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atguigu.admin.mapper.AccountMapper"> <!-- public Account getAcct(Long id); --><select id="getAcct" resultType="com.atguigu.admin.bean.Account">select * from account_tbl where id=#{id}</select> </mapper>
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
# 配置mybatis规则 mybatis: # config-location: classpath:mybatis/mybatis-config.xmlmapper-locations: classpath:mybatis/mapper/*.xmlconfiguration:map-underscore-to-camel-case: true可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
● 导入mybatis官方starter
● 编写mapper接口。标准@Mapper注解
● 编写sql映射文件并绑定mapper接口
● 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration) -
注解模式
@Mapper public interface CityMapper {@Select("select * from city where id=#{id}")public City getById(Long id);public void insert(City city);}
-
混合模式
@Mapper public interface CityMapper {@Select("select * from city where id=#{id}")public City getById(Long id);public void insert(City city);}
最佳实战:
● 引入mybatis-starter
● 配置application.yaml中,指定mapper-location位置即可
● 编写Mapper接口并标注@Mapper注解
● 简单方法直接注解方式
● 复杂方法编写mapper.xml进行绑定映射
● @MapperScan(“com.atguigu.admin.mapper”) 简化,其他的接口就可以不用标注@Mapper注解
1.1.4、整合Mybatis-Plus完成CRUD
-
什么是MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网
建议安装 MybatisX 插件 -
整合MyBatis-Plus
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version> </dependency>
自动配置
● MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
● SqlSessionFactory 自动配置好。底层是容器中默认的数据源
● mapperLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所 有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下
● 容器中也自动配置好了 SqlSessionTemplate
● @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行优点:
● 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力 -
CRUD功能
@GetMapping("/user/delete/{id}")public String deleteUser(@PathVariable("id") Long id,@RequestParam(value = "pn",defaultValue = "1")Integer pn,RedirectAttributes ra){userService.removeById(id);ra.addAttribute("pn",pn);return "redirect:/dynamic_table";}@GetMapping("/dynamic_table")public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){//表格内容的遍历 // response.sendError // List<User> users = Arrays.asList(new User("zhangsan", "123456"), // new User("lisi", "123444"), // new User("haha", "aaaaa"), // new User("hehe ", "aaddd")); // model.addAttribute("users",users); // // if(users.size()>3){ // throw new UserTooManyException(); // }//从数据库中查出user表中的用户进行展示//构造分页参数Page<User> page = new Page<>(pn, 2);//调用page进行分页Page<User> userPage = userService.page(page, null);// userPage.getRecords() // userPage.getCurrent() // userPage.getPages()model.addAttribute("users",userPage);return "table/dynamic_table";}
@Service public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {}public interface UserService extends IService<User> {}
1.2、NOSQL
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
1.2.1、Redis自动配置
```xml org.springframework.boot spring-boot-starter-data-redis ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ec9d74996f9c46638dec17a31b3ba66b.png) 自动配置: - RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis的配置 - 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration - 自动注入了RedisTemplateredis环境搭建
1、阿里云按量付费redis。经典网络
2、申请redis的公网连接地址
3、修改白名单 允许0.0.0.0/0 访问
1.2.2、RedisTemplate与Lettuce
@Test
void testRedis(){ValueOperations<String, String> operations = redisTemplate.opsForValue();operations.set("hello","world");String hello = operations.get("hello");System.out.println(hello);
}
1.2.3、切换至Jedis
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- 导入jedis-->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
spring:redis:host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.comport: 6379password: lfy:Lfy123456client-type: jedisjedis:pool:max-active: 10
单元测试
1、JUnit5 的变化
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit框架,JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
注意:
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)
JUnit 5’s Vintage Engine Removed from spring-boot-starter-test,如果需要继续兼容junit4需要自行引入vintage
<dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
@SpringBootTest
class Boot05WebAdminApplicationTests {@Testvoid contextLoads() {}
}
以前:
- @SpringBootTest + @RunWith(SpringTest.class)
SpringBoot整合Junit以后:
- 编写测试方法:@Test标注(注意需要使用junit5版本的注解)
- Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚
2、JUnit5常用注解
JUnit5的注解与JUnit4的注解有所变化
https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
- @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
- @RepeatedTest :表示方法可重复执行,下方会有详细介绍
- @DisplayName :为测试类或者测试方法设置展示名称
- @BeforeEach :表示在每个单元测试之前执行
- @AfterEach :表示在每个单元测试之后执行
- @BeforeAll :表示在所有单元测试之前执行
- @AfterAll :表示在所有单元测试之后执行
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories
- @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
- @ExtendWith :为测试类或测试方法提供扩展类引用
import org.junit.jupiter.api.Test; //注意这里使用的是jupiter的Test注解!!public class TestDemo {@Test@DisplayName("第一次测试")public void firstTest() {System.out.println("hello world");}
3、断言(assertions)
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别:
检查业务逻辑返回的数据是否合理。
所有的测试运行结束以后,会有一个详细的测试报告;
3.1、简单断言
方法名 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
@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());
}
3.2、数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
@Test
@DisplayName("array assertion")
public void array() {assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
3.3、组合断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
@Test
@DisplayName("assert all")
public void all() {assertAll("Math",() -> assertEquals(2, 1 + 1),() -> assertTrue(1 > 0));
}
3.4、异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
@Test
@DisplayName("异常测试")
public void exceptionTest() {ArithmeticException exception = Assertions.assertThrows(//扔出断言异常ArithmeticException.class, () -> System.out.println(1 % 0));}
3.5、超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
@Test
@DisplayName("超时测试")
public void timeoutTest() {//如果测试方法时间超过1s将会异常Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
3.6、快速失败
通过 fail 方法直接使得测试失败
@Test
@DisplayName("fail")
public void shouldFail() {fail("This should fail");
}
4、前置条件(assumptions)
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@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 对象才会被执行;当条件不满足时,测试执行并不会终止。
5、嵌套测试
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
@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 {@BeforeEachvoid 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";@BeforeEachvoid 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());}}}
}
6、参数化测试
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource: 表示为参数化测试提供一个null的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。
@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");
}
7、迁移指南
在进行迁移的时候需要注意如下的变化:
- 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
- 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
- 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
- 把@Ignore 替换成@Disabled。
- 把@Category 替换成@Tag。
- 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。
指标监控
1、SpringBoot Actuator
1.1、简介
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
1.2、1.x与2.x的不同
1.3、如何使用
- 引入场景
- 访问 http://localhost:8080/actuator/**
- 暴露所有监控信息为HTTP
management:endpoints:enabled-by-default: true #暴露所有端点信息web:exposure:include: '*' #以web方式暴露
登陆网页查看监控
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath
1.4、可视化
https://github.com/codecentric/spring-boot-admin
2、Actuator Endpoint
2.1、最常使用的端点
ID | 描述 |
---|---|
auditevents | 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件。 |
beans | 显示应用程序中所有Spring Bean的完整列表。 |
caches | 暴露可用的缓存。 |
conditions | 显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops | 显示所有@ConfigurationProperties。 |
env | 暴露Spring的属性ConfigurableEnvironment |
flyway | 显示已应用的所有Flyway数据库迁移。需要一个或多个Flyway组件。 |
health | 显示应用程序运行状况信息。 |
httptrace | 显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。 |
info | 显示应用程序信息。 |
integrationgraph | 显示Spring integrationgraph 。需要依赖spring-integration-core。 |
loggers | 显示和修改应用程序中日志的配置。 |
liquibase | 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。 |
metrics | 显示当前应用程序的“指标”信息。 |
mappings | 显示所有@RequestMapping路径列表。 |
scheduledtasks | 显示应用程序中的计划任务。 |
sessions | 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown | 使应用程序正常关闭。默认禁用。 |
startup | 显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup。 |
threaddump | 执行线程转储。 |
如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:
ID | 描述 |
---|---|
heapdump | 返回hprof堆转储文件。 |
jolokia | 通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core。 |
logfile | 返回日志文件的内容(如果已设置logging.file.name或logging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。 |
prometheus | 以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus。 |
最常用的Endpoint:
- Health:监控状况
- Metrics:运行时指标
- Loggers:日志记录
2.2、Health Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
重要的几点:
- health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告
- 很多的健康检查默认已经自动配置好了,比如:数据库、redis等
- 可以很容易的添加自定义的健康检查机制
2.3、Metrics Endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到;
- 通过Metrics对接多种监控系统
- 简化核心Metrics开发
- 添加自定义Metrics或者扩展已有Metrics
2.4、管理Endpoints
2.4.1、开启与禁用Endpoints
- 默认所有的Endpoint除过shutdown都是开启的。
- 需要开启或者禁用某个Endpoint。配置模式为 management.endpoint..enabled = true
management:endpoint:beans:enabled: true
2.4.2、暴露Endpoints
支持的暴露方式:
- HTTP:默认只暴露health和info Endpoint
- JMX:默认暴露所有Endpoint
- 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity,则会默认配置安全访问规则
ID | JMX | Web |
---|---|---|
auditevents | Yes | No |
beans | Yes | No |
caches | Yes | No |
conditions | Yes | No |
configprops | Yes | No |
env | Yes | No |
flyway | Yes | No |
health | Yes | Yes |
heapdump | N/A | No |
httptrace | Yes | No |
info | Yes | Yes |
integrationgraph | Yes | No |
jolokia | N/A | No |
logfile | N/A | No |
loggers | Yes | No |
liquibase | Yes | No |
metrics | Yes | No |
mappings | Yes | No |
prometheus | N/A | No |
scheduledtasks | Yes | No |
sessions | Yes | No |
shutdown | Yes | No |
startup | Yes | No |
threaddump | Yes | No |
3、定制 Endpoint
3.1、定制 Health 信息
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;@Component
public class MyHealthIndicator implements HealthIndicator {@Overridepublic Health health() {int errorCode = check(); // perform some specific health checkif (errorCode != 0) {return Health.down().withDetail("Error Code", errorCode).build();}return Health.up().build();}}构建Health
Health build = Health.down().withDetail("msg", "error service").withDetail("code", "500").withException(new RuntimeException()).build();
management:health:enabled: trueshow-details: always #总是显示详细信息。可显示每个模块的状态信息
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {/*** 真实的检查方法* @param builder* @throws Exception*/@Overrideprotected void doHealthCheck(Health.Builder builder) throws Exception {//mongodb。 获取连接进行测试Map<String,Object> map = new HashMap<>();// 检查完成if(1 == 2){
// builder.up(); //健康builder.status(Status.UP);map.put("count",1);map.put("ms",100);}else {
// builder.down();builder.status(Status.OUT_OF_SERVICE);map.put("err","连接超时");map.put("ms",3000);}builder.withDetail("code",100).withDetails(map);}
}
3.2、定制info信息
3.2.1、编写配置文件
info:appName: boot-adminversion: 2.0.1mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文件值mavenProjectVersion: @project.version@
3.2.2、编写InfoContributor
import java.util.Collections;import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;@Component
public class ExampleInfoContributor implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {builder.withDetail("example",Collections.singletonMap("key", "value"));}}
http://localhost:8080/actuator/info 会输出以上方式返回的所有info信息
3.3、定制Metrics信息
3.3.1、SpringBoot支持自动适配的Metrics
- JVM metrics, report utilization of:
- Various memory and buffer pools
- Statistics related to garbage collection
- Threads utilization
- Number of classes loaded/unloaded
- CPU metrics
- File descriptor metrics
- Kafka consumer and producer metrics
- Log4j2 metrics: record the number of events logged to Log4j2 at each level
- Logback metrics: record the number of events logged to Logback at each level
- Uptime metrics: report a gauge for uptime and a fixed gauge representing the application’s absolute start time
- Tomcat metrics (server.tomcat.mbeanregistry.enabled must be set to true for all Tomcat metrics to be registered)
- Spring Integration metrics
3.3.2、增加定制Metrics
class MyService{Counter counter;public MyService(MeterRegistry meterRegistry){counter = meterRegistry.counter("myservice.method.running.counter");}public void hello() {counter.increment();}
}//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
3.4、定制Endpoint
@Component
@Endpoint(id = "container")
public class DockerEndpoint {@ReadOperationpublic Map getDockerInfo(){return Collections.singletonMap("info","docker started...");}@WriteOperationprivate void restartDocker(){System.out.println("docker restarted....");}}
场景:开发ReadinessEndpoint来管理程序是否就绪,或者LivenessEndpoint来管理程序是否存活;
当然,这个也可以直接使用 https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-kubernetes-probes
原理解析
1、Profile功能
为了方便多环境适配,springboot简化了profile功能。
1.1、application-profile功能
- 默认配置文件 application.yaml;任何时候都会加载
- 指定环境配置文件 application-{env}.yaml
- 激活指定环境
- 配置文件激活
- 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
- 修改配置文件的任意值,命令行优先
- 默认配置与环境配置同时生效
- 同名配置项,profile配置优先
1.2、@Profile条件装配功能
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {// ...}
1.3、profile分组
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq使用:--spring.profiles.active=production 激活
2、外部化配置
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
- Default properties (specified by setting SpringApplication.setDefaultProperties).
- @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
- Config data (such as application.properties files)
- A RandomValuePropertySource that has properties only in random.*.
- OS environment variables.
- Java System properties (System.getProperties()).
- JNDI attributes from java:comp/env.
- ServletContext init parameters.
- ServletConfig init parameters.
- Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
- Command line arguments.
- properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
- @TestPropertySource annotations on your tests.
- Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
2.1、外部配置源
常用:Java属性文件、YAML文件、环境变量、命令行参数;
2.2、配置文件查找位置
- (1) classpath 根路径
- (2) classpath 根路径下config目录
- (3) jar包当前目录
- (4) jar包当前目录的config目录
- (5) /config子目录的直接子目录
2.3、配置文件加载顺序
- 1.当前jar包内部的application.properties和application.yml
- 2.当前jar包内部的application-{profile}.properties 和 application-{profile}.yml
- 3.引用的外部jar包的application.properties和application.yml
- 4.引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml
2.4、指定环境优先,外部优先,后面的可以覆盖前面的同名配置项
(5) /config子目录的直接子目录 > (4) jar包当前目录的config目录 > (3) jar包当前目录 > (2) classpath 根路径下config目录 > (1) classpath 根路径
3、自定义starter
3.1、starter启动原理
- starter-pom引入 autoconfigurer 包
- autoconfigure包中配置使用 META-INF/spring.factories 中 EnableAutoConfiguration 的值,使得项目启动加载指定的自动配置类
- 编写自动配置类 xxxAutoConfiguration -> xxxxProperties
- @Configuration
- @Conditional
- @EnableConfigurationProperties
- @Bean
- …
引入starter — xxxAutoConfiguration — 容器中放入组件 ---- 绑定xxxProperties ---- 配置项
3.2、自定义starter
atguigu-hello-spring-boot-starter(启动器)
atguigu-hello-spring-boot-starter-autoconfigure(自动配置包)
4、SpringBoot原理
Spring原理【Spring注解】、SpringMVC原理、自动配置原理、SpringBoot原理
4.1、SpringBoot启动过程
- 创建 SpringApplication
- 保存一些信息。
- 判定当前应用的类型。ClassUtils。Servlet
- bootstrappers:初始启动引导器(List):去spring.factories文件中找 org.springframework.boot.Bootstrapper
- 找 ApplicationContextInitializer;去spring.factories找 ApplicationContextInitializer
- List<ApplicationContextInitializer<?>> initializers
- 找 ApplicationListener ;应用监听器。去spring.factories找 ApplicationListener
- List<ApplicationListener<?>> listeners
- 运行 SpringApplication
- StopWatch
- 记录应用的启动时间
- 创建引导上下文(Context环境)createBootstrapContext()
- 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
- 让当前应用进入headless模式。java.awt.headless
- 获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
- getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
- 遍历 SpringApplicationRunListener 调用 starting 方法;
- 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
- 保存命令行参数;ApplicationArguments
- 准备环境 prepareEnvironment();
- 返回或者创建基础环境信息对象。StandardServletEnvironment
- 配置环境信息对象。
- 读取所有的配置源的配置属性值。
- 绑定环境信息
- 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
- 创建IOC容器(createApplicationContext())
- 根据项目类型(Servlet)创建容器,
- 当前会创建 AnnotationConfigServletWebServerApplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext()
- 保存环境信息
- IOC容器的后置处理流程。
- 应用初始化器;applyInitializers;
- 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
- 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
- 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
- 刷新IOC容器。refreshContext
- 创建容器中的所有组件(Spring注解)
- 容器刷新完成后工作?afterRefresh
- 所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
- 调用所有runners;callRunners()
- 获取容器中的 ApplicationRunner
- 获取容器中的 CommandLineRunner
- 合并所有runner并且按照@Order进行排序
- 遍历所有的runner。调用 run 方法
- 如果以上有异常,
- 调用Listener 的 failed
- 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
- running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
public interface Bootstrapper {/*** Initialize the given {@link BootstrapRegistry} with any required registrations.* @param registry the registry to initialize*/void intitialize(BootstrapRegistry registry);}
@FunctionalInterface
public interface ApplicationRunner {/*** Callback used to run the bean.* @param args incoming application arguments* @throws Exception on error*/void run(ApplicationArguments args) throws Exception;}
@FunctionalInterface
public interface CommandLineRunner {/*** Callback used to run the bean.* @param args incoming main method arguments* @throws Exception on error*/void run(String... args) throws Exception;}
4.2、Application Events and Listeners
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-application-events-and-listeners
ApplicationContextInitializer
ApplicationListener
SpringApplicationRunListener