了解自动配置原理
# 40.了解自动配置原理
通过上文的介绍,相信大家都知道使用 SpringBoot 是很方便的,这基于 SpringBoot 提供的两大特性:依赖管理,自动配置。
# 依赖管理
- 父项目做依赖管理
- 无需关注版本号,自动版本仲裁。也可以修改版本号
- 开发导入 starter 场景启动器
- ....
# 父项目做依赖管理
在 Maven 中,父项目常用做依赖管理。我们只需引入一个 SpringBoot 的父项目,就不用关心版本问题了。后续我们引入 starter,都不用写版本号。
我们可以在 IDEA 中按住 ctrl,然后点 pom.xml 文件中的父项目,可以看到父项目的内容:可以看到其还有一个父项目 spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
2
3
4
5
# 版本号
我们再点进去,可以看到里面配置了很多 properties:这就是配置了常用框架的版本号,包括 Spring 的,所以这也就是我们不用写版本号的原因。
<properties>
<activemq.version>5.15.13</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.82</appengine-sdk.version>
<artemis.version>2.12.0</artemis.version>
<aspectj.version>1.9.6</aspectj.version>
<assertj.version>3.16.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
............
2
3
4
5
6
7
8
9
10
一般来说,这些配置好的版本号,也是 SpringBoot 支持的版本号;但开发中,有时候要改版本号(例如漏洞更新),怎么改呢?
- 首先查看 spring-boot-dependencies 中配置的版本号是什么,使用的是什么标签
- 在项目的 pom.xml 中配置 properties 标签,里面写上版本号即可。
例如使用 MySQL5 的驱动,可以搜到 spring-boot-dependencies 是通过 <mysql.version>
这个标签来配置的,我们也使用该标签来改配置:
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
2
3
# starter 场景启动器
有时候我们 web 开发,使用 SpringMVC 时,不仅仅要引入 SpringMVC 的框架,还要 Spring 的依赖,日志的依赖等等;
在 SpringBoot 中,将所有 web 开发场景的依赖都整合到了一起,也就是 starter
。所以我们上一篇博客的案例,只引入 spring-boot-starter-web
,就能将 web 开发所需要的依赖都引入!
在 SpringBoot 中,有很多这样的 starter,官网 (opens new window) 是这样说的:
Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and related technologies that you need without having to hunt through sample code and copy-paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, include the
spring-boot-starter-data-jpa
dependency in your project.大意:Starters 是一个集合,包含了某个场景的依赖。
一般来说,官方的 starter 都是 spring-boot-starter-*
开头的,*
就代表着某种场景。
只要引入 starter,这个场景的所有常规需要的依赖我们都自动引入。其实原理也很简单,就是该 starter 项目里,引入了一些依赖而已,这是 Maven 的依赖传递。
在文档下方,还列出了所有 starter:几乎所有场景,SpringBoot 都有对应的 starter
Name | Description |
---|---|
spring-boot-starter | Core starter, including auto-configuration support, logging and YAML |
spring-boot-starter-activemq | Starter for JMS messaging using Apache ActiveMQ |
spring-boot-starter-amqp | Starter for using Spring AMQP and Rabbit MQ |
spring-boot-starter-aop | Starter for aspect-oriented programming with Spring AOP and AspectJ |
spring-boot-starter-artemis | Starter for JMS messaging using Apache Artemis |
spring-boot-starter-batch | Starter for using Spring Batch |
..... | ..... |
如果不满足你的需求,也可以自己写一个 starter:Creating Your Own Starter (opens new window),一般命名为 thirdpartyproject-spring-boot-starter
,当然一般用不上。
注意,所有 starter 的父项目都是 spring-boot-starter
,这是 SpringBoot 自动配置的核心依赖
# 自动配置
通过上篇博客的案例,我们知道 SpringBoot 帮我们配置了很多东西,例如:
- 自动配好 Tomcat
- 自动配好 SpringMVC
- 自动配好 Web 常见功能(例如字符串编码)
- 默认的包结构
- 各种配置都有默认值
- 按需加载所有自动配置项
- .....
# 自动配好 Tomcat
以 Tomcat 为例,自动配置,简单来说分为 2 步
- 引入 Tomcat 依赖
- 配置 Tomcat
例如,spring-boot-starter-web
里,有这样的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
2
3
4
5
6
至于第二步怎么配置的,我们后续再说
# 自动配好 SpringMVC
同理,spring-boot-starter-web
里,也有 SpringMVC 的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
我们之前使用 SpringMVC,都要配置 dispatcherServlet 和 characterEncodingFilter,那么 SpringBoot 有没配置呢?有的。我们修改下主程序,打印 IoC 容器里的内容:
package com.peterjxl.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 主程序类
* @SpringBootApplication 这是一个SpringBoot应用程序
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 2.查看容器里面的组件
String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
然后在打印的内容里,是能搜到 dispatcherServlet 和 characterEncodingFilter 的。
我们修改下 controller,试着返回中文:
package com.peterjxl.boot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // @RestController = @Controller + @ResponseBody
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "你好, Spring Boot 2!";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
试着访问:
同理,还配置了 viewResolver 和 multipartResolver 等等组件。
# 默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来,无需以前的包扫描配置
- 想要改变扫描路径:
@SpringBootApplication(scanBasePackages="com.atguigu")
,或者用@ComponentScan
指定扫描路径
@SpringBootApplication
// 等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.peterjxl.boot")
2
3
4
5
官方文档 (opens new window) 也有说到:
When a class does not include a
package
declaration, it is considered to be in the “default package”. The use of the “default package” is generally discouraged and should be avoided. It can cause particular problems for Spring Boot applications that use the@ComponentScan
,@ConfigurationPropertiesScan
,@EntityScan
, or@SpringBootApplication
annotations, since every class from every jar is read................
he following listing shows a typical layout:
com +- example +- myapplication +- MyApplication.java | +- customer | +- Customer.java | +- CustomerController.java | +- CustomerService.java | +- CustomerRepository.java | +- order +- Order.java +- OrderController.java +- OrderService.java +- OrderRepository.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 各种配置都有默认值
举个例子,要配置上传文件的大小:
spring.servlet.multipart.max-file-size=10MB
我们可以按住 ctrl 点进该配置项,可以看到其对应一个类:
@ConfigurationProperties(
prefix = "spring.servlet.multipart",
ignoreUnknownFields = false
)
public class MultipartProperties {
private DataSize maxFileSize = DataSize.ofMegabytes(1L);
.....
2
3
4
5
6
7
配置文件的值最终会绑定每个类上,这个类会在容器中创建对象(例如我们刚刚打印的内容中,就有 MultipartProperties 对象):
# 按需加载所有自动配置项
SpringBoot 有很多的 starter,难道全部都会加载配置吗?当然不是,引入了的 starter,该 starter 的自动配置才会开启
每个 starter 都依赖于 spring-boot-starter
,而其又依赖于 spring-boot-autoconfigure
,(可以通过看 pom.xml 文件分析出),所有自动配置的功能就是由 autoconfigure
来实现的
我们可以看这个 jar 包中的内容:
可以看到,这些就是各个 starter 的配置,例如 amqp,cache 等。
# 源码
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo2,读者可以通过切换分支来查看本文的示例代码