MyBatis整合
导入MyBatis整合的依赖:
<!-- <https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter> -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.1</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
在使用Spring Initialzr时,额外勾选到MyBatis Framework和MySql Driver即可
MyBatis整合
写好相应的mapper接口和mapper.xml文件后,在启动类上加上@MapperScan注解,在其中的basePackages属性指定要扫描的包的全限定符即可
@SpringBootApplication @MapperScan(basePackages = "com.ergou.boot3.ssm.mapper") public class Boot305SsmApplication {public static void main(String[] args) {SpringApplication.run(Boot305SsmApplication.class, args);}}
使用mybatis.mapper-locations指定每个mapper接口对应的xml文件在哪里,mybatis就会自动关联绑定(mapper接口的接口名和mapper.xml文件的文件名一致才会关联),例:
mybatis.mapper-locations=classpath:/mapper/*.xml
最后可以在配置文件中进行mybatis的一些额外配置,比如字段驼峰映射、类全限定符映射等
自动配置分析
- DataSourceAutoConfiguration配置了数据源等基本信息,会自动配置数据源(和数据源相关的配置都绑定在DataSourceProperties)
- 导入了jdbc的场景,给jdbcTemplate导入了dataSource的信息
- 导入了和事务有关的数据源,支持了事务
- MyBatisAutoConfiguration配置了MyBatis的整合流程(和MyBatis相关的配置绑定在MybatisProperties)
- 给容器中准备了SqlSessionFactory等组件,以操作数据库
- 在数据源配置好后再配置mybatis
- MapperScan原理:利用Import(MapperScannerRegistrar.class)批量给容器中注册组件。解析指定包中的mapper接口,为其每一个mapper接口创建bean定义信息,注册到容器中
SpringBoot3基础特性
自定义banner
banner就是springboot启动时在控制台上出现的那个由字符拼接成的那个spring图标
自定义banner:
- 在配置文件中的spring.banner.location属性指定其classpath,在项目下放一个banner的文本图标的文件,然后写在classpath:后即可
自定义SpringApplication
可以在SpringApplication的run操作前,插入一些自定义设置(这些设置在配置文件中也可以起作用)
@SpringBootApplication
public class Boot306FeaturesApplication {public static void main(String[] args) {//可以拆分springApplication的run操作,在中间插入自定义的底层设置SpringApplication springApplication = new SpringApplication(Boot306FeaturesApplication.class);//例:设置banner的模式springApplication.setBannerMode(Banner.Mode.CONSOLE);// SpringApplication运行起来springApplication.run(args);}
}
还可以通过SpringApplicationBuilder:
@SpringBootApplication
public class Boot306FeaturesApplication {public static void main(String[] args) {new SpringApplicationBuilder().main(Boot306FeaturesApplication.class).sources(Boot306FeaturesApplication.class).bannerMode(Banner.Mode.CONSOLE).listeners(……).run(args);}}
但是:配置文件的的优先级高于程序化调整的优先级
Profiles
指定环境
- Spring Profiles 提供一种隔离配置的方式,使其仅在特定环境生效;
- 环境:dev(开发环境)、test(测试环境)、prod(生产环境)
- 默认环境:default,即在所有环境都生效。未标注@Profile,即为default环境。
- 任何@Component, @Configuration 或 @ConfigurationProperties 可以使用 @Profile 标记,来指定何时被加载。【容器中的组件都可以被 @Profile标记】
- 在@Profile注解的value属性中指定要生效的环境即可(value是Stirng类型的数组,可以指定多个环境)
激活环境
- 通过配置文件激活
spring.profiles.active=dev
- 也可以使用命令行激活,在使用命令行运行时加上--spring.profiles.active=dev
- 还可以配置默认环境; 不标注@Profile 的组件永远都存在。以前默认环境叫default,使用
spring.profiles.default=test
配置后,默认环境为test- 不推荐使用修改默认环境,建议配置active激活环境
- 还可以使用包含环境
- 配置spring.profiles.include=环境1,环境2。则环境1和环境总是激活
- 用法:
- 基础的配置
mybatis
、log
、xxx
:写到包含环境中- 需要动态切换变化的
db
、redis
:写到激活的环境中
环境分组
可以使用 spring.profiles.group.组名 对多个环境进行分组,再对组进行active激活,就可以激活组中的相应的多个环境
spring.profiles.group.aaaa=dev,test
spring.profiles.active=aaaa
Profile配置文件
- application.properties是主配置文件,在任何情况下都生效。
- 可以使用application-环境标识.properties配置文件(例如:application-dev.properties、application-test.properties等)
- 指定Profile配置文件只会在其特定环境下生效
- 如果Profile配置文件中的配置和主配置文件中的配置有冲突,则以Profile配置文件的配置为优先
- spring.profiles.active 和spring.profiles.default 只能用到非Profile配置文件中,如果在application-dev.yaml中编写就是无效的
外部化配置
- SpringBoot 使用 配置优先级 + 外部配置 简化配置更新、简化运维。
- 只需要给jar应用所在的文件夹放一个application.properties最新配置文件,重启项目就能自动应用最新配置
配置优先级
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(Profile配置文件)
- jar 包外的application.properties/yml
- jar 包外的application-{profile}.properties/yml(Profile配置文件)
即:包外大于包内,Profile配置文件大于主配置文件
SpringBoot 应用启动时会自动寻找application.properties和application.yaml位置,进行加载。顺序如下:(后面覆盖前面,由低到高)
- 类路径: 内部
- 类根路径
- 类下/config包
- 当前路径(项目所在的位置)
- 当前路径
- 当前下/config子目录
- /config目录的直接子目录
最终效果:优先级由高到低,前面覆盖后面
- 命令行 > 包外config直接子目录 > 包外config目录 > 包外根目录 > 包内目录
- 同级比较:
- profile配置 > 默认配置
- properties配置 > yaml配置
配置的优先级规律:最外层的最优先。
- 命令行 > 所有
- 包外 > 包内
- config目录 > 根目录
- profile > application
配置不同就都生效(互补),配置相同高优先级覆盖低优先级
导入配置
使用spring.config.import属性可以导入额外配置文件中的配置,属性值写其他配置文件的文件名即可
spring.config.import=classpath:/xxxx.properties
例:
application.properties内容如下:
spring.config.import=classpath:/abc.properties server.port=8888
abc.properties内如如下:
server.port=8889
结论:服务端口是8889
属性占位符
配置文件中可以使用 ${name:default}形式取出之前配置过的值。
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
:后表示默认值,以上为例,如果找不到username,则${username:Unknown}的结果为Unknown
单元测试
SpringBoot 提供一系列测试工具集及注解方便我们进行测试
相关注解:
@SpringBootTest
- 在测试类上加上@SpringBootTest注解后,测试方法运行时,会启动boot项目测试。
- 加上@SpringBootTest后,在测试类中,可以使用整个项目中的组件,只要在测试类中进行使用@Autowired注解进行组件注入即可
@Test
表示该方法是测试方法
@DisplayName
给测试方法命名其展示的测试名
@BeforeAll
标注了BeforeAll注解的方法,所有测试方法在运行之前,先运行此方法
@BeforeEach
标注了BeforeEach的方法,每个测试方法运行之前,先运行此方法
@AfterAll、@AfterEach
和BeforeAll和BeforeEach类似,将之前改为之后即可
断言
测试时可以使用断言,来进行一些判断
与断言有关的方法如下:
方法 | 说明 |
---|---|
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 {@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());}}}
}
参数化测试
- 参数化测试是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");//返回一个流
}