Spring Boot 原理分析
SpringBoot 原理分析
文章目录
一、起步依赖原理分析
1.1 分析spring-boot-starter-parent
按住 Ctrl 点击 pom.xml 中的 spring-boot-starter-parent
,跳转到了 spring-boot-starter-parent
的 pom.xml,xml 配置如下(只摘抄了部分重点配置):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
按住 Ctrl 点击 pom.xml 中的 spring-boot-starter-dependencies
,跳转到了 spring-boot-starter-dependencies
的 pom.xml,xml 配置如下(只摘抄了部分重点配置):
<properties>
<activemq.version>5.15.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.63</appengine-sdk.version>
<artemis.version>2.4.0</artemis.version>
<aspectj.version>1.8.13</aspectj.version>
<assertj.version>3.9.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.7.11</byte-buddy.version>
... ... ...
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
... ... ...
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
</plugin>
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.1.RELEASE</version>
</plugin>
... ... ...
</plugins>
</pluginManagement>
</build>
从上面的 spring-boot-starter-dependencies
的 pom.xml 中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的 SpringBoot
工程继承 spring-boot-starter-parent
后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。
1.2 分析spring-boot-starter-web
spring-boot-starter-web
是 Spring Boot
场景启动器,帮我们导入了 web 模块正常运行所依赖的组件。
Spring Boot
将所有的功能场景都抽取出来,做成一个个的 starters(启动器)。只需要在项目中引入这些 starter, 相关场景的所有依赖都会导入进来,要用什么功能就导入什么场景的启动器。
按住 Ctrl 点击 pom.xml 中的 spring-boot-starter-web
,跳转到了 spring-boot-starter-web
的 pom.xml,xml 配置如下(只摘抄了部分重点配置):
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
<name>Spring Boot Web Starter</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
从上面的 spring-boot-starter-web 的 pom.xml 中我们可以发现,spring-boot-starter-web 就是将 web 开发要使用的 spring-web、spring-webmvc 等坐标进行了“打包”,这样我们的工程只要引入 spring-boot-starter-web 起步依赖的坐标就可以进行 web 开发了,同样体现了依赖传递的作用。
二、自动配置原理
2.1 自动配置原理解析
主程序的 @SpringBootApplication
注解标注在某个类上就说明这个类是 Spring Boot
的主配置类,Spring Boot
就应该运行这个类的 main 方法来启动 Spring Boot
应用。
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class);
}
}
按住 Ctrl 点击查看启动类上的注解 @SpringBootApplication
,就可以看到注解 @SpringBootApplication
的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
... ... ...
}
其中:
-
@SpringBootConfiguration:等同与
@Configuration
,即标注该类是 Spring 的一个配置类。由@Confituration
标注的类本质上也属于容器中的一个@Component
。 -
@EnableAutoConfiguration:Spring Boot 自动配置功能开启。以前我们需要配置的东西,如今 Spring Boot 帮我们自动配置了。
按住 Ctrl 点击查看注解 @EnableAutoConfiguration
:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
... ... ...
}
其中:
-
@AutoConfigurationPackage:自动配置包。
进入
@AutoConfigurationPackage
可以看到如下配置:@Import({Registrar.class}) public @interface AutoConfigurationPackage { ... ... ... }
@Import({Registrar.class}):Spring 的底层注解
@Import
,给容器中导入一个组件。导入的组件为Registrar.class
,由它将主配置类所在的包以及下面所有子包的所有组件扫描到 Spring 容器中。 -
@Import(AutoConfigurationImportSelector.class):
AutoConfigurationImportSelector
是指定导入哪些组件的选择器。它会将所有需要导入的组件以全类名的方式返回,这些组件将会被添加到容器中。按住 Ctrl 点击查看
AutoConfigurationImportSelector
源码:protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; }
其中,
loadFactoryNames()
方法的作用就是从META-INF/spring.factories
文件中读取指定类对应的类名称列表 :spring.factories
文件中有关自动配置的配置信息如下:... ... ... org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ ... ... ...
上面配置文件存在大量的以 Configuration 为结尾的类名称,这些类就是存有自动配置信息的类。
Spring Boot 在启动的时候从上面的配置文件中获取
EnableAutoConfiguration
指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作 。有了自动配置类,就免去了之前我们使用 Spring 框架需要配置大量繁琐的 XML 的工作,使我们更专注于功能的开发。
例:
我们以 ServletWebServerFactoryAutoConfiguration
为例来分析源码:
// 表示这是一个配置类,可以给容器中添加组件
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 判断当前项目有没有 ServletRequest 这个类,如果有,当前配置类才生效。
@ConditionalOnClass(ServletRequest.class)
// 判断当前应用是否是 web 应用,如果是,当前配置类才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 启动指定类的 ConfigurationProperties 功能,就是把配置文件的配置数据注入到类中
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
... ... ...
}
其中:
@ConditionalOnXXX:该注解会根据给定的条件判断,决定这个配置类是否生效。一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的 properties 类中获取的,这些类里面的每一个属性都是和配置文件绑定的。
@EnableConfigurationProperties(ServerProperties.class):该注解代表加载 ServerProperties
服务器配置属性类。
进入 ServerProperties.class
, 源码如下:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
... ... ...
}
其中:
prefix = "server"
表示 SpringBoot 配置文件中的前缀,SpringBoot 会将配置文件中以 server 开始的属性映射到该类的字段中。映射关系如下:
2.2 Spring Boot 自动配置的精髓
- Spring Boot 启动会加载大量的自动配置类
- 我们看我们需要的功能有没有已经被 Spring Boot 提供的自动配置类默认写好
- 如果有,我们再来看这个自动配置类中到底配置了哪些组件,如果有我们要用的组件,就无需我们手动配置了
- 如果没有,我们给容器中自动配置类添加组件的时候,会从
Properties
类中获取某些属性,所以我们可以在配置文件中指定这些属性的值。
2.3 细节
2.3.1 @Conditional 派生注解
@Conditional
派生注解的作用是:必须 @Conditional
注解所指定的条件成立,该注解所修饰的配置类里面的所有内容才能生效,才给容器中添加组件。
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的 Java 版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定 Bean |
@ConditionalOnMissingBean | 容器中不存在指定 Bean |
@ConditionalOnExpression | 满足 SpEL 表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的 Bean,或者这个 Bean 是首选 Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是 web 环境 |
@ConditionalOnNotWebApplication | 当前不是 web 环境 |
@ConditionalOnJndi | JNDI 存在指定项 |
2.3.2 查看自动配置类是否生效
我们可以通过在主配置文件中配置 debug
属性来达到让控制台打印自动配置报告的目的,这样我们就可以方便的知道哪些自动配置类生效了。
debug = true
在主配置文件中配置好 debug
属性之后,启动 Spring Boot,查看控制台自动配置报告:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:(自动配置类启用的)
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.AspectJAutoProxyingConfiguration matched:
- @ConditionalOnClass found required class 'org.aspectj.weaver.Advice' (OnClassCondition)
... ...
Negative matches:(没有启用,没有匹配成功的自动配置类)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
... ...
推荐阅读
-
Spring Boot面试题(2020最新版)
-
单点登录 Ucenter 原理分析
-
Mybatis源码分析——运行原理及流程
-
MySQL中Join算法实现原理分析[组图]_MySQL
-
PHP strtotime函数用法、实现原理和源码分析_php技巧
-
解决spring boot1.5以上版本@ConfigurationProperties提示“Spring Boot Configuration Annotation Processor not.."
-
Base64编码原理分析与PHP实现_PHP教程_编程技术
-
什么是Spring Boot
-
Spring-Boot 集成Solr客户端的详细步骤
-
Spring Boot实现图片上传功能