# Spring Boot
# 参考文档
# 前置知识
- Spring
- Maven
# 要求
这里以 Spring Boot 2.5.3 为例,官方对系统的配置要求。
- Java 8
- Spring 5.3.9+
- Maven 3.5+
PS
Spring 5 基于 Java 8,Java 8 有一个新特性,接口有了默认实现。 在 Java 8 之前,如果一个接口有 5 个方法,但是有两个实现类, 实现类三只想实现前两个方法,而实现类二只想实现后三个方法。 那么这时候实现类一除了它要实现的两个方法外,也要实现后三个方法的空实现。 所以,会采用适配器解决这个问题,即适配器(Adapter)实现所有的接口方法,实现类再分别重写适配器(Adapter)。 但是 Java 8 的新特性(接口可以有默认实现 default),直接解决了这种问题,所以,Adapter 就过时了。
# 简介
Spring Boot 是简化 Spring 开发的一个框架,简化 Spring 技术栈的快速开发脚手架,是整个 Spring 技术栈的大整合。
# 手动创建一个 SpringBoot 应用
TIP
这里以创建一个 web 项目为例:
创建一个 Maven 项目
在 POM 文件中 添加:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.4</version> </parent>
1
2
3
4
5因为这是一个 web 工程,所以还需要添加:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
1
2
3
4
5
6创建包结构,
com.yyy.xxx
,在 xxx 下编写你的应用- 必须在
java
包写创建自己的包,然后主启动类必须放在你自己创建的包下,否则会报错。(Spring Boot 的约定)
- 必须在
在 xxx 下创建主程序类:
// 该注解是告诉 SpringBoot 框架,当前是一个 SpringBoot 应用,当前类是一个主程序类。 @SpringBootApplication public class MyApplication { public static void main(String[] args) { // 让 Spring 启动起来 SpringApplication.run(MyApplication.class, args); } }
1
2
3
4
5
6
7
8在 xxx 下创建 controller 包
在 controller 包下创建 Controller 类:
// @RestController = @Controller + @ResponseBody @RestController public class DemoController { // 路由映射 @RequestMapping("/") public String hello() { return "hello world"; } }
1
2
3
4
5
6
7
8
9在 resources 包下创建 application.properties 或者 application.yml 配置文件:
<!-- 这里以创建 application.properties 为例 --> server.port = 8801
1
2运行
MyApplication.main()
启动工程
# <parent>
<dependencyManagement>
标签
使用 <parent>
标签可以继承指定的依赖。
比如创建一个 Spring Boot 项目的第一步就是引入:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
</parent>
2
3
4
5
点进去可以发现这个 spring-boot-starter-parent
中已经引入了 spring-boot-starter-web
,
而且使用 <parent>
标签可以继承指定的依赖,那么为什么还要在自己的 POM.xml 中引入依赖 spring-boot-starter-web
,
项目才可以启动呢。
因为 <dependencyManagement> </dependencyManagement>
,这个标签的作用是管理依赖,所以在 POM.xml 中引入依赖 spring-boot-starter-web
的时候,
不再需要填写版本号,因为不写版本号的时候 maven 会沿着 <parent>
向上一直找到带有这个标签的依赖为止。
那么不写版本号可以理解,为什么使用了 <parent>
标签已经继承依赖,为什么还要自己写一遍呢,因为 <dependencyManagement> </dependencyManagement>
不会被继承,不会被引入。所以在父模块的 POM 中还需要再添加 web 依赖,但是这个父模块的子模块则只需要在子模块 POM 中使用 <parent>
引入父模块,就不需要再引入 web 了,因为它已经继承了父模块的 web,如果父模块中使用 <dependencyManagement>
将引入的 web 管理,那么在子模块中还需要再次引入 web 才能使用。
# 可执行 jar
# 创建步骤
打包使用
spring-boot-maven-plugin
,在 POM 引入如下:<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
1
2
3
4
5
6
7
8clean
->install
->java -jar <jar-name>
# fat jar
spring boot 应用打的包就是 fat jar。
就是将当前的程序和它依赖的 jar 打包在一起称为一个 jar。
# 自动配置
# 自动配置-POM 分析
- starter 是指一类包集合。
- 官方的包命名
spring-boot-starter-*
,这里的*
指的是场景- 例如
spring-boot-starter-web
指的是 web 场景所能使用到的所有包的一个集合。
- 例如
- 第三方包命名
*-spring-boot-starter
# 自动配置-包扫描
默认的包扫描规则,是扫描主类所在的包下的所有文件以及子包。
# 自动配置-组件
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
// 返回 Spring 容器实例
ConfigurableApplicationContext run = SpringApplication.run(BootApplication.class, args);
// 获取 Spring Boot 启动以后,加载的所有组件,即初始化的所有 Bean
for (String beanDefinitionName : run.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
}
2
3
4
5
6
7
8
9
10
11
# 自动配置-注解
# @Configuration
@Configuration
注解类在容器中是一个代理类。
- 替代配置文件,使用
@Bean
注解进行 Bean 的注册。 @Bean
只会执行一次,默认是单例。- proxyBeanMethods 属性:
- true,单例(默认)
- false,多例
Full,全配置,即 @Configuration(proxyBeanMethods = true)
。
Lite,轻量级配置,即 @Configuration(proxyBeanMethods = false)
。
@Configuration
注解的配置类有如下要求:
@Configuration
不可以是 final 类型;@Configuration
不可以是匿名类;- 嵌套的 configuration 必须是静态类。
@Configuration
注解的 spring 容器加载方式,用 AnnotationConfigApplicationContext
替换 ClassPathXmlApplicationContext
示例:ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
# @Import
使用在被 Spring 管理的组件上面。
- 参数,class array 类型
- 通过创建无参构造的方式导入类实例到容器
@ImportResource
可以导入 Spring 的 XML 配置文件到 Config 类。(主要用于升级中)
# 通过 ImportSelector 方式导入类
不同于 @Configuration
+ @Bean
的方式将 Bean 加载进容器,也可以使用 @Import
+ ImportSelector 的方式加载。
示例:
package com.aa.model;
public class User {
private String name;
...
}
---
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{
"com.aa.model.User",
...
};
}
}
---
@Import({MySelector.class})
@Configuration
public class ImportConfig {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# @Conditional
条件装配,符合条件才进行装配,如果将这个注解加载配置类上,表示当前的配置文件中是否符合当前条件,否则整个配置文件的 Bean 都不会被注册。
自定义装配条件,编写一个类 MyCondition implements Condition
,重写 matches 方法,自定义逻辑,true 就注册,false 就不会注册。
还有其它 Spring 提供的注解:
@ConditionalOnBean
,如果存在这个 Bean 的话,才进行注入。@ConditionalOnClass
- ...
示例:
// 这里只注册了 stu2conditional
@Configuration
public class StuConfig {
public Stu stu1() {
return new Stu();
}
@Bean
public Stu stu2() {
return new Stu();
}
}
---
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
boolean stu1 = run.containsBean("stu1");
System.out.println("stu1 是否存在" + stu1);
boolean stu2 = run.containsBean("stu2");
System.out.println("stu2 是否存在" + stu2);
}
}
---
stu1 是否存在 false
stu2 是否存在 true
---
@Configuration
public class StuConfig {
public Stu stu1() {
return new Stu();
}
@Bean
@ConditionalOnBean(name = "stu1")
public Stu stu2() {
return new Stu();
}
}
---
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
boolean stu1 = run.containsBean("stu1");
System.out.println("stu1 是否存在" + stu1);
boolean stu2 = run.containsBean("stu2");
System.out.println("stu2 是否存在" + stu2);
}
}
---
stu1 是否存在false
stu2 是否存在false
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
# @ConfigurationProperties
配置绑定: 将配置文件(application.properties 或者 application.yml)中的内容绑定到 JavaBean 中。
注意: 这个注解只能使用在 Component 上
使用方式:
@Bean
+@ConfigurationProperties
实现application.yml: user: name: 666 --- @Data public class AaaProperties { private String name; } --- public class XxxConfig() { @Bean @ConfigurationProperties(prefix = "user") public AaaProperties aProperties () { AaaProperties a = new AaaProperties(); a.setName("999"); return a; } } --- 实际结果: a.getName = 666 详见 ConfigurationProperties 实现原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Component
+@ConfigurationProperties
实现@EnableConfigurationProperties(XxxProperty.class)
+@ConfigurationProperties
实现@EnableConfigurationProperties(XxxProperty.class)
作用:- 相当于是开启配置绑定 + 将传入类注册到容器
- 适用于第三方 Bean
注意:
@Component
+@ConfigurationProperties
时,容器中注入的将会是一个 name=xxxProperty 的 Bean;- 但是
@EnableConfigurationProperties(XxxProperty.class)
+@ConfigurationProperties
的方式注入的 Bean 名不会是类名首字母小写的形式,而是 name=xxx-com.a.b.XxxProperty。
# ConfigurationProperties 实现原理
当在一个 bean 组件上使用了 @ConfigurationProperties
注解时,指定的配置属性将会被设置到该 bean。
这一注解背后的工作机制,主要是由框架通过 BeanPostProcessor
ConfigurationPropertiesBindingPostProcessor
来完成的。
ConfigurationPropertiesBindingPostProcessor
的作用是绑定 PropertySources
到 @ConfigurationProperties
注解的 bean 实例。
这是一个框架内部工具,在实例化每一个 bean 时,框架会使用它将 @ConfigurationProperties
注解中指定前缀的外部配置属性项加载进来设置到 bean 相应的属性上。
这里的 PropertySources
能够从上下文中的 Environment
对象获取外部配置属性项。
# @SpringBootApplication
@SpringBootApplication
相当于以下三个注解:
@SpringBootConfiguration
,实质就是个@Configuration
,实质就是个 Component@ComponentScan
,扫描指定范围/包下的组件@EnableAutoConfiguration
- 借助
@Import
的支持,收集和注册特定场景相关的 bean 定义 @AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration
实质: 从 classpath 中搜寻所有META-INF/spring.factories
配置文件,并将其中org.spring-framework.boot.autoconfigure.EnableAutoConfiguration
对应的配置项通过反射(Java Reflection)实例化为对应的标注了@Configuration
的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。- 借助
# 自动配置原理
Spring Boot 启动的时候,默认加载进来所有的 AutoConfiguration 类,(通过 @Import
加载进来所有的配置类)
这些配置类都是 @Configuration
+ @Conditionalxxx
组合,按条件进行加载的。
即:扫入全部配置文件 -> 按需加载
# 自动配置-启动分析
# 自动配置项分析Import
打印 CONDITIONS EVALUATION REPORT,条件评估报告。
配置文件中配置 debug=true
,开启调试模式,然后启动程序,这时候控制台可以打印自动配置了那些配置。
# starter
spring 的 starter 依赖会将指定场景可能用到的所有依赖添加进去。
例如 spring-boot-starter-web
会将 tomcat 等相关依赖全部集成进去,也就是集成了在 web 场景下你所能用到的所有依赖。
# Testing
Spring Boot Testing 引入的依赖:spring-boot-starter-test
中包含了很多测试相关的类库:
- JUnit 5: The de-facto standard for unit testing Java applications.
- Spring Test & Spring Boot Test: Utilities and integration test support for Spring Boot applications.
- AssertJ: A fluent assertion library.
- Hamcrest: A library of matcher objects (also known as constraints or predicates). // other assertion library
- Mockito: A Java mocking framework.
- JSONassert: An assertion library for JSON.
- JsonPath: XPath for JSON.
JsonPath.read(json,"$.store.book[1].author");
# 测试 Service
# 测试 Controller
# 讨论区
由于评论过多会影响页面最下方的导航,故将评论区做默认折叠处理。