SpringBoot框架原理分析

SpringBoot框架原理分析

原文链接

目录

一、起步依赖原理分析

1、spring-boot-starter-parent

2、spring-boot-starter-web

二、自动配置原理解析

1、@SpringBootApplication注解

[【1】@SpringBootConfiguration 注解](https://onestar.newstar.net.cn/blog/29#【1】%40SpringBootConfiguration 注解)

[【2】@EnableAutoConfiguration 注解](https://onestar.newstar.net.cn/blog/29#【2】%40EnableAutoConfiguration 注解)

[【3】@ComponentScan 注解](https://onestar.newstar.net.cn/blog/29#【3】%40ComponentScan 注解)

一、起步依赖原理分析

在搭建SpringBoot环境的时候(SpringBoot入门:https://blog.csdn.net/One_L_Star/article/details/103033571),在pom.xml中添加了两个依赖,对这两个依赖进行分析,分别是:

  • SpringBoot的起步依赖:spring-boot-starter-parent

  • web的起步依赖:spring-boot-starter-web

1、spring-boot-starter-parent

spring-boot-starter-parent 是Spring Boot的父级依赖,是一个特殊的starter,它用来提供相关的Maven默认依赖。使用它之后,常用的包依赖可以省去version标签。

咱们可以直接对源码进行分析,来看看源码,就拿上篇博文中搭建 SpringBoot 环境的代码,使用 idea 按住 Ctrl 点击 pom.xml 中的spring-boot-starter-parent

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

截取了部分源码,在这里,可以看到SpringBoot的继承关系:

  • SpringBoot 继承 spring-boot-starter-parent
  • spring-boot-starter-parent 继承 spring-boot-dependencies

【1】咱们可以进入 spring-boot-dependencies 看一看,按住 Ctrl 点击 spring-boot-dependencies,截取部分代码:

<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-parent定义的版本来提供相应版本的匹配,这就很好的解决了Spring导入版本依赖冲突的问题,所以我们的 SpringBoot 工程继承 spring-boot-starter-parent 后已经具备版本锁定等配置了。

可以看出起步依赖的作用就是进行依赖的传递。

【2】在 spring-boot-starter-parent 中,还有一个地方咱们可以看一下,那就是资源引入:

<resource>
    <filtering>true</filtering>
    <directory>${basedir}/src/main/resources</directory>
    <includes>
        <include>**/application*.yml</include>
        <include>**/application*.yaml</include>
        <include>**/application*.properties</include>
    </includes>
</resource>

可以看到,${basedir}/src/main/resources 表示资源的加载文件,资源文件包括下面三种格式的,也就是说,咱们在配置SpringBoot资源文件的时候都是以 application*.yml、application*.yaml、application*.properties文件格式

2、spring-boot-starter-web

spring-boot-starter-web 是web功能的起步依赖,导入了web功能的起步依赖后,可以不用导入Spring和SpringMVC的坐标,是因为starter-web 将坐标打包了,同样,可以来看看源码,按住 Ctrl 点击 spring-boot-starter-web,截取部分代码

<?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就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。

二、自动配置原理解析

自动配置其实就是将默认的配置自动加载进去,不需要我们去手动进行配置,在这里,我们还是以我的上一篇博客的环境搭建程序为例(https://blog.csdn.net/One_L_Star/article/details/103033571),依然是对源码进行分析,从mySpringBootApplication引导类开始:

@SpringBootApplication
public class mySpringBootApplication {
    public static void main(String[] args) {
        //将字节码引导参数传递给run方法
        SpringApplication.run(mySpringBootApplication.class);
    }
}

1、@SpringBootApplication注解

首先是@SpringBootApplication注解,咱们按住 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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};
 
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};
 
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};
 
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

在这里我们可以看到,有一些其他的注解,咱们挑一些重要的注解来进行分析:

【1】@SpringBootConfiguration 注解

咱们可以按住 Ctrl 点进去看

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

可以看到 @SpringBootConfiguration 注解上面有一个@Configuration 注解,既标注该类是 Spring 的一个配置类 ,其实,@SpringBootConfiguration 注解就相当于Configuration注解,用于标注该类是 Spring 的一个配置类,和 Spring 中的@Configuration 注解是一个意思

【2】@EnableAutoConfiguration 注解

@EnableAutoConfiguration 注解是 SpringBoot 自动配置功能开启 ,同样,咱们可以按住 Ctrl 点进去瞧瞧

@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 {};
}

可以看到,在上面有一个 @Import({AutoConfigurationImportSelector.class}) 注解配置,这是导入了AutoConfigurationImportSelector类,意思是自动配置导入选择器,咱们可以点进AutoConfigurationImportSelector类看看,截取部分源码:

img

图中框出来的表示加载某些配置,点进源码看一看:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

其中,SpringFactoriesLoader.loadFactoryNames 方法的作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表,而这个文件就在当前类的包下面,咱们可以找到看一看:

img

点进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 为结尾的类名称,这些类就是存有自动配置信息的类,而 SpringApplication 在获取这些类名后再加载 ,我们以ServletWebServerFactoryAutoConfiguration为例来分析源码,找到 ServletWebServerFactoryAutoConfiguration 点进去看看:

@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {...}

在这里,@EnableConfigurationProperties(ServerProperties.class) 代表加载 ServerProperties 服务器配置属性类,咱们可以进入ServerProperties.class 看源码(截取部分源码):

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    ...
}

在这里,prefix = "server" 表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类的字段中。可以通过这个类完成相应配置加载,而加载的内容在包下面的 spring-configuration-metadata.json 文件中

img

点进去可以看到很多默认的配置,咱们以 server.port 配置为例:

{
  "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
  "defaultValue": 8080,
  "name": "server.port",
  "description": "Server HTTP port.",
  "type": "java.lang.Integer"
}

这个配置表示 Tomcat 启动时的默认端口,在这里配好之后,通过 ServerProperties 进行加载,然后通过@EnableConfigurationProperties标签引入,最终在 getCandidateConfigurations 类中进行加载。这就是整个默认配置的过程,当然,我们可以通过修改我们自己的 application*.properties 配置文件来覆盖默认配置

【3】@ComponentScan 注解

@ComponentScan 注解是用于组件扫描,默认扫描当前引导类所在的包下的索引

end

评论