文章目录
- 数据访问
- 示例
- 自动配置原理
- jdbc场景自动配置数据源等基本信息
- `MyBatisAutoConfiguration`配置MyBatis整合流程
- 基础特性
- `SpringApplication`
- 自定义`banner`
- 自定义`SpringApplication`
- FluentBuilder API
- Profiles
- 使用
- 指定环境
- 环境激活
- 环境包含
- Profiles配置文件
- 外部化配置
- 配置优先级
- 外部配置
- 导入配置
- 属性占位符
- 单元测试-JUnit5
- 整合
- 测试
- 注解
- 断言
- 核心原理
- 事件和监听器
- 生命周期监听
- 监听器
- 生命周期全流程
- 事件触发时机
- 各种回调监听器
- 完整触发流程
- SpringBoot事件驱动开发
- 自动配置原理
- 入门
- 自动配置流程
- SPI机制
- 功能开关
- 进阶
- `@SpringBootApplication`
- 完整启动加载流程
- 自定义`starter`
- 业务代码
- 基本抽取
- 使用`Enable`机制
- 完全自动
数据访问
整个SSM场景:Spring、SpringMVC、MyBatis
示例
- 依赖
<!--ssm-->
<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>
<version>8.0.30</version>
<scope>runtime</scope>
</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/ssm
spring.datasource.username=root
spring.datasource.password=root
- 可用MyBatisX插件,帮助生产Mapper接口的xml文件
- 配置MyBatis
# 指定mapper映射文件位置
mybatis.mapper-locations=classpath:/mapper/*.xml
# 参数项调整为驼峰命名,便于匹配字段与数据项
mybatis.configuration.map-underscore-to-camel-case=true
- CRUD
- 每个方法都应在
Mapper
文件中有一个sql标签与之对应- 所有参数都应用
@Param
进行签名,以后使用指定签名在SQL中取值- 使用
@MapperScan
(批量扫描)告知MyBatis,扫描哪个包下的所有接口- 使用
mybatis.mapper-locations
告知MyBatis,接口对应的xml文件在哪里- MyBatis自动关联由接口全类名等同namespace值来实现
- sql写在xml中,mybatis配置写在
application.properties
中
自动配置原理
jdbc场景自动配置数据源等基本信息
mybatis-spring-boot-starter
导入spring-boot-starter-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
:
- 基于XA二阶提交协议的分布式事务数据源
org.springframework.boot.autoconfigure.jdbc.DataSourceTransacitonManagerAutoConfiguration
:
- 支持事务
- 底层能力:数据源、
JdbcTemplate
、事务
MyBatisAutoConfiguration
配置MyBatis整合流程
mybatis-spring-boot-starter
导入mybatis-spring-boot-autoconfigure
(mybatis的自动配置包)- 默认配置类:
org.springframework.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
org.springframework.boot.autoconfigure.MybatisAutoConfiguration
:
- 必须在数据源配置成功后才进行配置
- 向容器中放置
SqlSessionFactory
组件,创建和数据库的一次会话- 向容器中放置
SqlSessionTemplate
组件,操作数据库
- MyBatis的所有配置绑定在
MyBatisProperties
- 利用
@MapperScan
原理,将Mapper接口的代理对象创建并放入容器:
- 利用
@Import(MapperScannerRegister.class)
批量向容器中注册组件,解析指定包路径中的所有类,为所有Mapper接口类创建Bean信息,并注册到容器中
基础特性
SpringApplication
自定义banner
- 类路径添加
banner.txt
或设置spring.banner.location
可以设置banner样式 spring.main.banner-mode=off
可以快速关闭banner
自定义SpringApplication
@SpringBootApplication
public class MyApplication {
public static void main(string[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
FluentBuilder API
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
Profiles
期待快速切换开发、测试、生产环境的环境隔离能力:
- 标识环境:指定对应组件、配置在对应位置生效
- 切换环境:使用特定环境时,对应的所有组件和配置则生效
使用
指定环境
- Spring Profiles提供一种隔离配置的方式,使其仅在特定环境下生效
- 任何
@Component
、@Configuration
或@ConfigurationProperties
都可以使用@Profile
进行标记,指定何时被加载
@Profile({"test"})
- 不被
@Profile
标记的组件代表任意时刻都生效,不区分环境
环境激活
- 配置激活环境
spring.profiles.active=production,hsqldb
or
spring.profiles.active[0]=production
spring.profiles.active[1]=hsqldb
- 命令行激活
java -jar xxx.jar --spring.profile.active=development,hsqldb
- 配置默认环境,未指明时使用该环境对应组件和配置(推荐使用激活方式选择场景,而非指定默认方式固定使用某个场景)
spring.profiles.default=test
环境包含
- 包含场景:无论在何种情况下都会生效的场景
spring.profiles.include=test, development
spring.profiles.active
和spring.profiles.default
只能用到无profile的文件中,若在application-test.yaml
中编写则是无效指令- 可以额外添加生效文件,而非激活替换
spring.profiles.include[0]=common
spring.profiles.include[1]=local
- 最终生效的场景 = 激活/默认场景 + 包含场景
- 在项目中:
- 基础的配置
mybatis
、log
、xxx
等写入包含场景- 动态切换变化的配置
db
、redis
等写入激活/默认场景
Profiles配置文件
application.properties
主配置文件,任何情况下生效- 命名规范:
application-{profile标识}.properties
- 激活指定场景使用,项目生效配置 = 激活场景配置文件的所有项 + 主配置文件和激活文件不冲突的所有项(冲突项以激活场景配置文件为准)
外部化配置
线上应用快速修改配置,至最新配置
- SpringBoot使用 配置优先级+外部配置 简化配置更新、简化运维
- 给jar应用所在文件夹放置
application.properties
最新配置文件,重启项目即可自动应用最新配置
配置优先级
SpringBoot允许将配置外部化,以便在不同环境中使用相同的应用程序代码
- 对SpringBoot属性源的加载顺序,优先级顺序为(数字与优先级相反):
优先级 | 属性原 |
---|---|
14 | 默认属性(通过SpringApplication.setDefaultProperties 指定) |
13 | @PropertySource 指定加载配置(需写在@Configuration 类上) |
12 | 配置文件(`application.properties/yml等) |
11 | RandomValuePropertySource 支持的random.* 配置(eg.`@Value("$(random.int)*)) |
10 | OS环境变量 |
9 | Java系统属性(System.getProperties() ) |
8 | JNDI属性(来自java:comp/env ) |
7 | ServletContext 初始化参数 |
6 | ServletConfig 初始化参数 |
5 | SPRING_APPLICATION_JSON 属性(内置在环境变量或系统属性中的JSON) |
4 | 命令行参数 |
3 | 测试属性(@SpringBootTest 测试时指定的属性) |
2 | 测试类@TestPropertySource 注解 |
1 | Devtools设置的全局属性($HOME/.config/spring-boot ) |
- 常用
- 命令行 > 配置文件 >
springapplication
配置- 配置文件的优先级:
- 包外 > 包内;
profile
>application
.properties
>.yml
- config目录 > 根目录
外部配置
导入配置
#导入额外配置,值总是优先于文件内部编写的值
spring.boot.import=classpath:/*.properties
属性占位符
#去除曾配置过的项的值
#Unknown代表默认值,选填
app.name=MyApp
app.description={$name:dault} is a Spring Boot application written by ${username:Unknown)
单元测试-JUnit5
整合
SpringBoot存在一系列测试工具及注解方便用户调用测试
spring-boot-test
提供核心测试能力、spring-boot-test-autoconfigure
提供部分测试配置
<!--test单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
测试
组件测试,直接使用@Autowired
容器组件注入
注解
注解 | 用法 |
---|---|
@Test | 标识为测试方法,但不同于Junit4的同名注解,该注解不能声明任何属性,拓展测试将由Jupiter提供额外测试进行 |
@Parameterized | 标识参数化测试方法 |
@Repeated | 标识可重复执行方法 |
@DisplayName | 为测试类或测试方法设置展示名 |
@BeforeEach | 表示在每个单元测试前执行 |
@AfterEach | 表示在每个单元测试后执行 |
@BeforeAll | 表示在所有单元测试前执行 |
@AfterAll | 表示在所有单元测试后执行 |
@Tag | 标识单元测试类型,类似于Junit4的@Categories |
@Disabled | 标识不执行该测试类或测试方法,类似于Junit4的@Ignore |
@Timeout | 标识测试方法最大运行时间及超时会返回错误 |
@ExtendWith | 测试类或测试方法拓展类引用 |
断言
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否值相同对象 |
assertNotSame | 判断两个对象引用是否值不同对象 |
asserTrue | 判断给定布尔值是否为true |
asserFalse | 判断给定布尔值是否为false |
asserNull | 判断给定对象引用是否为null |
asserNotNull | 判断给定对象引用是否不为null |
assertArrayEquals | 数组断言 |
assertAll | 组合断言 |
assertThrows | 异常断言 |
assertTimeout | 超时断言 |
fall | 快速失败 |
核心原理
事件和监听器
生命周期监听
监听应用的生命周期
监听器
- 自定义
SpringApplicationRunListener
来监听事件:
- 编写
SpringApplicationRunListener
实现类- 在
META-INF/spring.factories
中配置org.springframework.boot.SpringApplicationRunListener=个人Listener
,还可指定一个有参构造器,接受参数(SpringApplication application, String[] args)
- SpringBoot在
spring-boot.jar
中配置了默认Listener:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
- 引导:利用 BootstrapContext 引导整个项目启动
starting
:应用开始,SpringApplication
的run
方法调用,只要存在BootstrapContext
就执行environmentPrepared
:环境准备完毕(将启动参数等绑定到环境变量中),但IoC还未创建
- 启动:
contextPrepared
:IiC容器创建且准备完毕,但主配置类sources
未加载,关闭引导启动器contextLoaded
:IoC容器与主配置类加载完毕,但IoC容器还未刷新(Bean还未创建)started
:IoC容器刷新(Bean准备完毕),但runner
未调用ready
:IoC容器刷新(Bean准备完毕),runner
调用完毕
- 运行:步骤正确执行后,容器开始
running
生命周期全流程
事件触发时机
各种回调监听器
BootstrapRegistryInitializer
:感知遇到初始化阶段
META-INF/spring.factories
- 创建引导上下文
bootstrapContext
的时候触发- 场景:进行密钥校对授权
ApplicationContextInitializer
:感知IoC容器初始化阶段
META-INFO/spring.factories
ApplicationListener
:感知全阶段,基于事件机制感知事件,处于某个阶段允许执行对应操作
@Bean
或@EventListener
:事件驱动SpringApplication.addListeners()
或SpringApplicationBuilder.listeners()
META-INF/spring.factories
SpringApplicationRunListener
:感知全阶段,在任意阶段都能够自定义操作,更加完善的功能覆盖面
META-INF/spring.factories
ApplicationRunner
:感知特定阶段,感知到应用就绪时Ready;感知到应用卡死,不进入就绪
@Bean
CommandLineRunner
:感知特定阶段,感知到应用就绪时Ready;感知到应用卡死,不进入就绪
@Bean
- 示例:
- 若项目启动前操作:
BootstrapRegistryInitializer
和ApplicationContextInitializer
- 若项目启动后操作:
ApplicationRunner
和CommandLineRunner
- 若要干涉生命周期操作:
SpringApplicationRunListener
- 若要使用事件机制:
ApplicationListener
完整触发流程
事件 | 触发时机 |
---|---|
ApplicationStartingEvent | 应用启动但未做任何操作,越过注册listeners and initializers |
ApplicationEnvironmentPreparedEvent | 环境准备好,但context 未创建 |
ApplicationContextInitializedEvent | ApplicationContext 准备好,ApplicationContextInitializers 调用,单位加载任何Bean |
ApplicationPreparedEvent | 容器刷新之前,Bean定义信息加载 |
ApplicationStartedEvent | 容器刷新完成,runner 未调用 |
AvailabilityChangeEvent | LivenessState.CORRECT 应用存活—存活探针 |
ApplicationReadyEvent | 任何runner 被调用 |
AvailabilityChangeEvent | ReadinessState.ACCEPTING_TRAFFIC 接收请求—就绪探针 |
ApplicationFailedEvent | 启动出错 |
SpringBoot事件驱动开发
应用启动过程生命周期事件感知(9种给定情况)、应用运行中事件感知(无数种自定义)
- 事件发布:
ApplicationEventPublishAware
或注入ApplicationEventMulticaster
- 事件监听:
组件 + @EventListener
自动配置原理
入门
应用关注核心:场景、配置、组件
自动配置流程
- 导入
starter
- 导入依赖
autoconfigure
- 寻找类路径
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件 - 加载所有自动配置类
*AutoConfiguration
- 给容器中配置功能组件
- 组件参数绑定到属性类中
*Properties
- 属性类和配置文件前缀绑定
@Conditional
派生条件注解判定组件是否生效
- 效果:
- 修改配置文件、底层参数
- 所有场景自动配置直接使用
- 可注入SpringBoot准备好的组件直接使用
SPI机制
- SPI(Service Provider Interface)是一种软件设计模式,用于在应用程序中动态发现和加载组件
- SPI思想定义一个接口或抽象类,再通过
classpath
中定义好的实现该接口的类,解决对组件的动态发现和加载 - Java中SPI由
META-INF/services
目录下创建一个以服务接口全限定名为名的文件,其中包含实现该服务接口的类的全限定名,Java SPI会自动扫描classpath
并根据文件中指定的类名来加载实现类 - SPI机制使得程序更加灵活、可扩展,同时增加代码可维护性和避免硬编码依赖关系
功能开关
- 自动配置:自动化,项目启动时spi文件指定的所有内容都加载
@Enable*
:手动控制开启*
功能,利用@Import
将需要的功能单独导入
进阶
@SpringBootApplication
@SpringBootConfiguration
:就是@Configuration
,容器中的组件、配置类,spring IoC启动即创建该类对象@EnableAutoConfiguration
:开启自动配置@AutoConfigurationPackage
:扫描主程序包:加载自身的组件
- 利用
@Import(AutoConfigurationPackages.Registrar.class)
给容器中导入需要的组件- 把主程序所在的包的所有组件都导入进来
@Import(AutoConfigurationImportSelector.class)
:加载所有自动配置类:starter
导入的组件@ComponentScan
:组件扫描,排除已扫描进来的非必要配置类和自动配置类
完整启动加载流程
自定义starter
- 创建自定义
starter
项目,引入spring-boot-starter
依赖 - 编写模块功能,并引入模块所需依赖,编写
*AutoConfiguration
自动配置类 - 编写配置文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
,指定启动时需要加载的自动配置 - 等待其他项目引入
业务代码
自定义配置显示提示:导入依赖并重启项目,再次填写配置文件时跳出补完提示
<!--导入配置处理器,使配置文件properties配置显示补完提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
@ConfigurationProperties(prefix="robot")//此属性类和配置文件指定前缀绑定
@Component
@Data
public class RobotProperties {
private String name;
private String emial;
}
基本抽取
- 创建
starter
项目,把公共代码需要的所有依赖导入 - 复制公共代码
- 自定义一个
RobotAutoConfiguration
,向容器中放置该场景所需的所有组件(因为starter
所在的包和引入它的项目的主程序所在的包并非父子层级,所有这些组件不会被默认扫描到) - 其他用户引用该
starter
时,直接导入这个RobotAutoConfiguration
,就能将该场景的组件导入
使用Enable
机制
其他用户引用该starter
时,使用@EnableRobot
开启对应功能
@Retention(RetentionPolicy.RUNTIME)
@Target({ElemenType.TYPE})
@Documented
@Import(RobotAutoConfiguration.class)
public @interface EnableRobot {...}
完全自动
依赖SpringBoot的SPI机制
- 在
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中编写好自定义的自动配置类的全类名- 项目启动时,自动加载声明的自动配置类