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-webSpring 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 自动配置的精髓
  1. Spring Boot 启动会加载大量的自动配置类
  2. 我们看我们需要的功能有没有已经被 Spring Boot 提供的自动配置类默认写好
  3. 如果有,我们再来看这个自动配置类中到底配置了哪些组件,如果有我们要用的组件,就无需我们手动配置了
  4. 如果没有,我们给容器中自动配置类添加组件的时候,会从 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 环境
@ConditionalOnJndiJNDI 存在指定项
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)
   ... ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值