SpringBoot 整合
Spring
、SpringMVC
、MyBatis
进行数据访问场景开发
勾选之后会导入以下包
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>3.0.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
安装MyBatisX 插件,帮我们生成Mapper接口的xml文件即可
在接口处 : Alt + 回车
必须把mapper的xml文件映射位置在配置文件定义!
而驼峰命名转换建议也开启
#指定mapper映射文件位置
mybatis.mapper-locations=classpath:/mapper/*.xml
#驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true
package com.atguigu.boot.ssm.bean;
import lombok.Data;
@Data
public class TUser {
private Integer id;
private String loginName;
private String nickName;
private String passwd;
}
public interface UserMapper {
public TUser getUserById(@Param("id") Long id);
}
mybatisx
插件,快速生成MapperXML
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.boot.ssm.mapper.UserMapper">
<select id="getUserById" resultType="com.atguigu.boot.ssm.bean.TUser">
select * from t_user where id=#{id}
select>
mapper>
UserController
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
@GetMapping("/user/{id}")
public TUser getUser(@PathVariable("id") Long id){
TUser user = userMapper.getUserById(id);
return user;
}
}
/**
* 1.@MapperScan告诉Mybatis,扫描哪个包下面的所有接口
* 2.使用mybatis.mapper-locations 告诉mybatis,每个接口的xml文件都在哪里
*/
@MapperScan(basePackages = "com.atguigu.boot.ssm.mapper")
@SpringBootApplication
public class Boot05SsmApplication {
public static void main(String[] args) {
SpringApplication.run(Boot05SsmApplication.class, args);
}
}
运行结果:
- 导入
mybatis-spring-boot-starter
- 配置数据源信息
- 配置mybatis的
mapper接口扫描
与xml映射文件扫描
- 编写bean,mapper,生成xml,编写sql 进行crud。事务等操作依然和Spring中用法一样
- 效果:
- 所有sql写在xml中
- 所有mybatis配置写在application.properties
下面
jdbc场景的自动配置
:
mybatis-spring-boot-starter
导入 spring-boot-starter-jdbc
,jdbc是操作数据库的场景
Jdbc
场景的几个自动配置
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
DataSourceProperties
HikariDataSource
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
JdbcTemplate
操作数据库org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
具有的底层能力:数据源、JdbcTemplate
、事务
MyBatisAutoConfiguration
:配置了MyBatis的整合流程
mybatis-spring-boot-starter
导入 mybatis-spring-boot-autoconfigure(mybatis的自动配置包)
,
默认加载两个自动配置类:
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
SqlSessionFactory
组件。创建和数据库的一次会话SqlSessionTemplate
组件。操作数据库MyBatis的所有配置绑定在MybatisProperties
每个Mapper接口的代理对象是怎么创建放到容器中。详见**@MapperScan**原理:
@Import(MapperScannerRegistrar.class)
批量给容器中注册组件。解析指定的包路径里面的每一个类,为每一个Mapper接口类,创建Bean定义信息,注册到容器中。如何分析哪个场景导入以后,开启了哪些自动配置类。
找:
classpath:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中配置的所有值,就是要开启的自动配置类,但是每个类可能有条件注解,基于条件注解判断哪个自动配置类生效了。
快速定位生效的配置
#开启调试模式,详细打印开启了哪些自动配置
debug=true
# Positive(生效的自动配置) Negative(不生效的自动配置)
暂不支持 SpringBoot3
druid-starter
Druid官网
#数据源基本配置
spring.datasource.url=jdbc:mysql://192.168.200.100:3306/demo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 配置StatFilter监控
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.filter.stat.db-type=mysql
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000
# 配置WallFilter防火墙
spring.datasource.druid.filter.wall.enabled=true
spring.datasource.druid.filter.wall.db-type=mysql
spring.datasource.druid.filter.wall.config.delete-allow=false
spring.datasource.druid.filter.wall.config.drop-table-allow=false
# 配置监控页,内置监控页面的首页是 /druid/index.html
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
spring.datasource.druid.stat-view-servlet.allow=*
# 其他 Filter 配置不再演示
# 目前为以下 Filter 提供了配置支持,请参考文档或者根据IDE提示(spring.datasource.druid.filter.*)进行配置。
# StatFilter
# WallFilter
# ConfigFilter
# EncodingConvertFilter
# Slf4jLogFilter
# Log4jFilter
# Log4j2Filter
# CommonsLogFilter
附录:示例数据库
CREATE DATABASE IF NOT EXISTS test;
USE test;
CREATE TABLE `t_user`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`login_name` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户名称' COLLATE 'utf8_general_ci',
`nick_name` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户昵称' COLLATE 'utf8_general_ci',
`passwd` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户密码' COLLATE 'utf8_general_ci',
PRIMARY KEY (`id`)
);
insert into t_user(login_name, nick_name, passwd) VALUES ('zhangsan','张三','123456');
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
//1.SpringApplication:Boot应用的核心API入口
//SpringApplication.run(Boot306FeaturesApplication.class,args);
//1.自定义SpringApplication的底层设置
SpringApplication application = new SpringApplication(MyApplication.class);
//程序化调整SpringApplication的参数
//application.setDefaultProperties();
//这个配置不优先
//2.调整SpringApplication的参数
application.setBannerMode(Banner.Mode.OFF);
//3.SpringApplication运行起来
application.run(args);
}
流式方式启动SpringApplication
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
环境隔离能力;快速切换开发、测试、生产环境
步骤:
- 标识环境:指定哪些组件、配置在哪个环境生效
- 切换环境:这个环境对应的所有组件和配置就应该生效
@Component
, @Configuration
或 @ConfigurationProperties
可以使用 @Profile
标记,来指定何时被加载。【容器中的组件都可以被 @Profile
标记】1.配置激活指定环境; 配置文件
spring.profiles.active=production,hsqldb
2.也可以使用命令行激活。--spring.profiles.active=dev,hsqldb
3.还可以配置默认环境; 不标注@Profile 的组件永远都存在。
spring.profiles.default=test
4.推荐使用激活方式激活指定环境
注意:
spring.profiles.active
和spring.profiles.default
只能用到 无 profile 的文件中,如果在application-dev.yaml
中编写就是无效的spring.profiles.include[0]=common
spring.profiles.include[1]=local
最佳实战:
生效的环境 = 激活的环境/默认环境 + 包含的环境
项目里面这么用
mybatis
、log
、xxx
:写到包含环境中db
、redis
:写到激活的环境中创建prod
组,指定包含db
和mq
配置
spring.profiles.group.prod[0]=db
spring.profiles.group.prod[1]=mq
使用--spring.profiles.active=prod
,就会激活prod
,db
,mq
配置文件
application-{profile}.properties
可以作为指定环境的配置文件。
激活这个环境,配置就会生效。最终生效的所有配置是
application.properties
:主配置文件,任意时候都生效application-{profile}.properties
:指定环境配置文件,激活指定环境生效如果发生配置冲突:profile优先级 > application
场景:线上应用如何快速修改配置,并应用最新配置?
- SpringBoot 使用 配置优先级 + 外部配置 简化配置更新、简化运维。
- 只需要给
jar
应用所在的文件夹放一个application.properties
最新配置文件,重启项目就能自动应用最新配置
Spring Boot 允许将配置外部化,以便可以在不同的环境中使用相同的应用程序代码。
我们可以使用各种外部配置源,包括Java Properties文件、YAML文件、环境变量和命令行参数。
@Value可以获取值,也可以用@ConfigurationProperties将所有属性绑定到java object中
以下是 SpringBoot 属性源加载顺序。 后面的会覆盖前面的值。由低到高,高优先级配置覆盖低优先级
SpringApplication.setDefaultProperties
指定的)application.properties/yml
等)RandomValuePropertySource
支持的random.*
配置(如:@Value(“${random.int}”))System.getProperties()
)java:comp/env
)ServletContext
初始化参数ServletConfig
初始化参数SPRING_APPLICATION_JSON
属性(内置在环境变量或系统属性中的 JSON)@SpringBootTest
进行测试时指定的属性)@TestPropertySource
注解$HOME/.config/spring-boot
)结论:配置可以写到很多位置,常见的优先级顺序:
命令行
> 配置文件
> springapplication配置
配置文件优先级如下:(后面覆盖前面)
jar
包内的application.properties/yml
application-{profile}.properties/yml
application.properties/yml
application-{profile}.properties/yml
建议:用一种格式的配置文件。如果.properties和.yml同时存在,则.properties优先
结论:包外 > 包内
; 同级情况:profile配置 > application配置
所有参数均可由命令行传入,使用--参数项=参数值
,将会被添加到环境变量中,并优先于配置文件
。
比如java -jar app.jar --name="Spring"
,可以使用@Value("${name}")
获取
演示场景:
server.port=8000
server.port=9000
server.port=8001
server.port=9001
启动端口?:命令行 > 9001
> 8001
> 9000
> 8000
SpringBoot 应用启动时会自动寻找application.properties和application.yaml位置,进行加载。顺序如下:(后面覆盖前面)
1.类路径: 内部
2.当前路径(项目所在的位置)
最终效果:优先级由高到低,前面覆盖后面
命令行 > 包外config直接子目录 > 包外config目录 > 包外根目录 > 包内目录
同级比较:
规律:最外层的最优先。
配置不同就都生效(互补),配置相同高优先级覆盖低优先级
使用spring.config.import
可以导入额外配置
spring.config.import=my.properties
my.property=value
无论以上写法的先后顺序,my.properties
的值总是优先于直接在文件中编写的my.property。
配置文件中可以使用 ${name:default}
形式取出之前配置过的值。
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
SpringBoot 提供一系列测试工具集及注解方便我们进行测试。
spring-boot-test
提供核心测试能力,spring-boot-test-autoconfigure
提供测试的一些自动配置。
我们只需要导入spring-boot-starter-test
即可整合测试
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
spring-boot-starter-test
默认提供了以下库供我们测试使用
直接@Autowired
容器中的组件进行测试
JUnit5的注解与JUnit4的注解有所变化
https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class StandardTests {
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@DisplayName("")
@Test
void succeedingTest() {
}
@Test
void failingTest() {
fail("a failing test");
}
@Test
@Disabled("for demonstration purposes")
void skippedTest() {
// not executed
}
@Test
void abortedTest() {
assumeTrue("abc".contains("Z"));
fail("test should have been aborted");
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
}
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
assertArrayEquals | 数组断言 |
assertAll | 组合断言 |
assertThrows | 异常断言 |
assertTimeout | 超时断言 |
fail | 快速失败 |
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 {
@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());
}
}
}
}
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource
等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource
: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource
: 表示为参数化测试提供一个null的入参
@EnumSource
: 表示为参数化测试提供一个枚举入参
@CsvFileSource
:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource
:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
@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");
}