Spring Boot原理分析
Spring Boot依赖管理
spring-boot-starter-parent依赖
在pom.xml文件中找到spring-boot-starter-parent依赖,示例代码如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
使用Ctrl+鼠标左键可进入并查看spring-boot-starter-parent底层源文件,可发现spring-boot-starter-parent的底层有一个父依赖spring-boot-starter-dependencies,代码如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
继续查看spring-boot-starter-dependencies底层源文件:
<properties>
<activemq.version>5.15.12</activemq.version>
...
<solr.version>8.2.0</solr.version>
<spring-amqp.version>2.2.5.RELEASE</spring-amqp.version>
<spring-batch.version>4.2.1.RELEASE</spring-batch.version>
<spring-cloud-connectors.version>2.0.7.RELEASE</spring-cloud-connectors.version>
...
<spring-security.version>5.2.2.RELEASE</spring-security.version>
<spring-session-bom.version>Corn-SR2</spring-session-bom.version>
<spring-ws.version>3.0.8.RELEASE</spring-ws.version>
<sqlite-jdbc.version>3.28.0</sqlite-jdbc.version>
<sun-mail.version>${jakarta-mail.version}</sun-mail.version>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
<thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
<thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
<thymeleaf-layout-dialect.version>2.4.1</thymeleaf-layout-dialect.version>
<tomcat.version>9.0.33</tomcat.version>
...
</properties>
回到spring-boot-starter-parent底层源文件,可发现一个includes标签,代码如下:
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
从spring-boot-starter-dependencies底层源文件可以看出spring-boot-starter-parent是通过 properties 标签对一些常用技术框架的依赖文件进行统一版本号管理。
同时,在执行过程中帮助我们加载了指定的配置文件。
spring-boot-starter-web依赖
在pom.xml文件中找到spring-boot-starter-web依赖,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同样,进入spring-boot-starter-web并查看其底层文件,可以看到多个 dependency 标签,部分代码如下:
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.2.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.2.6.RELEASE</version>
<scope>compile</scope>
</dependency>
...
从上述代码可以发现,spring-boot-starter-web依赖启动器的主要作用是提供Web开发场景所需的低层所有文件,对Web开发场景所需的依赖文件进行了统一管理。
Spring Boot自动配置
Spring Boot自动配置的实现
- Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法;
- @SpringBootApplication能够扫描Spring组件并自动配置Spring Boot
- @SpringBootApplication注解是一个组合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个核心注解
进入@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}
)}
)
其中核心注解为@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。
- @SpringBootConfiguration:表示被这个注解所配置的类,为核心配置类,Ctrl+左键进入注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
因为在Spring中被@Configuration标注的类就为Spring的核心配置类,因此@SpringBootConfiguration类其实就是对@Configuration进行了一个封装。
- @EnableAutoConfiguration:Spring Boot自动配置的关键注解,表示开启自动配置功能。进入注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
其中,真正起作用的是@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})这两个注解。
@AutoConfigurationPackage:自动配置包;
@Import:导入其他的配置类,在@Import({AutoConfigurationImportSelector.class})这个配置类中,查看AutoConfigurationImportSelector类的getAutoConfigurationEntry()方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
这个方法的作用是自动将一些配置类进行扫描导入。
- @ComponentScan:进行包扫描。(注意:扫描的是项目启动类所在的包及其子包,所以自己创建的类应该要放在项目启动类所在的包及其子包的目录下。)
Spring Boot执行流程
Spring Boot的执行流程主要分为两步:
- 初始化Spring Application实例
- 初始化Spring Boot项目启动
Spring Boot执行流程
每个Spring Boot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main()方法,在该方法中通过执行SpringApplication.run()方法即可启动整个Spring Boot程序。
public class Chapter01Application {
public static void main(String[] args) {
SpringApplication.run(Chapter01Application.class, args);
}
}
点击进入run()方法:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
调用了另一个run()方法,接着进入该run()方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
在这个run()方法中,有两个作用:
- 创建了SpringApplication的实例对象;
- 再次调用run()方法。