文末送书 | 带你搞定 Spring Boot 核心运行原理

武培轩

共 8014字,需浏览 17分钟

 · 2020-08-06












前言

Spring Boot 最核心的功能就是“自动配置”,这一切都基于“约定优于配置”的原则。那么 Spring Boot 是如何约定,又是如何实现自动配置功能的呢?

下面带大家来通过源码学习 Spring Boot 的核心运作原理以及最核心的注解 @EnableAutoConfiguration 来进行讲解。

1.1 核心运行原理

使用 Spring Boot 时我们只需引入对应的 Starters,Spring Boot 启动时便自会动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对第三方软件进行集成,这便是 Spring Boot 的自动配置功能。先从整体上看一下 Spring Boot 实现该运作机制涉及到的核心部分,如下图。

Spring Boot 核心运行原理

图中描述了 Spring Boot 自动配置运作过程中涉及的几个核心功能及其相互之间的关系。

它们包括:@EnableAutoConfigurationspring.factories、各组件对应的 AutoConfiguration 类、@Conditional 注解以及各种 Starters

如果用一句话来描述整个过程就是:Spring Boot 通过 @EnableAutoConfiguration 注解开启自动配置,加载 spring.factories 中注册的各种 AutoConfiguration 类,当某个 AutoConfiguration 类满足其注解 @Conditional 指定的生效条件(Starters 提供的依赖、配置或 Spring 容器中是否存在某个 Bean 等)时,那么实例化该 AutoConfiguration 类中定义的 Bean(组件等),并注入 Spring 容器,至此就完成了依赖框架的自动配置。

先从概念及功能上了解一下上图所属部分的作用及相互关系。

@EnableAutoConfiguration:该注解由组合注解 @SpringBootApplication 引入,完成自动配置开启,扫描各个jar包下的 spring.factories 文件,并加载其中注册的 AutoConfiguration 类等。

spring.factories:配置文件,位于 jar 包的META-INF目录下,按照指定格式注册了自动配置的 AutoConfiguration 类,也可以包含其他类型待注册的类。该配置文件不仅存在于 Spring Boot 项目中,也可以存在于自定义的自动配置(或Starter)项目中。

AutoConfiguration 类:自动配置类,代表了 Spring Boot 中一类以XXAutoConfiguration命名的自动配置类,其中定义了三方组件集成 Spring 所需初始化的 Bean 和条件。

@Conditional:条件注解及其衍生注解,使用在 AutoConfiguration 类上,当满足该条件注解时才会实例化AutoConfiguration类。

Starters:三方组件的依赖及配置,Spring Boot 已经预置的组件。Spring Boot 默认的 Starters 项目往往是只包含了一个pom依赖的项目。如果自定义的 starter,该项目还需包含spring.factories文件、AutoConfiguration类和其他配置类。

以上在概念层面了解了 Spring Boot 自动配置的整体流程和基本运作原理,下面重点看一下 @EnableAutoConfiguration注解的功能。

1.2 运作原理源码解析之 @EnableAutoConfiguration

@EnableAutoConfiguration 是开启自动配置的注解,在创建的 Spring Boot 项目中并不能直接看到此注解,它是由组合注解@SpringBootApplication引入。下面,我们首先了解一下入口类和@SpringBootApplication注解,然后再深入了解@EnableAutoConfiguration注解的构成与作用。

1.2.1入口类和 @SpringBootApplication注解

Spring Boot 项目创建完成会默认生成一个*Application的入口类。默认情况下,无论通过 IDEA 或通过官网创建基于 Maven 的 Spring Boot 项目,入口类的命名规则为:artifactId+Application。通过该类的 main 方法即可启动 Spring Boot 项目。

@SpringBootApplication
public class SpringLearnApplication {
 public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

关于 main 方法并无特别之处,就是一个标准的 Java 应用的 main 方法,这里是启动 Spring Boot 项目的入口。默认情况下,按照上述命名规则并包含 main 方法的类,我们以下称作“入口类”。

在入口类中,我们可以看到 Spring Boot 创建的代码中(除单元测试外)唯一的一个注解就是@SpringBootApplication。它是 Spring Boot 项目的核心注解,用于开启自动配置,准确说是该注解内组合的@EnableAutoConfiguration开启了自动配置。

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

// 指定是否代理@Bean方法以强制执行bean的声明周期行为。
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}

通过源代码可以看出,该注解提供了以下成员属性(注解中的成员变量以方法的形式体现):

exclude:根据类(Class)排除指定的自动配置,该成员属性覆盖了@SpringBootApplication 中组合的@EnableAutoConfiguration中定义的exclude成员属性。

excludeName:根据类名排除指定的自动配置,同样覆盖了@EnableAutoConfiguration中的excludeName的成员属性。

scanBasePackages:指定扫描的基础 package,用于激活@Component等注解类的初始化。

scanBasePackageClasses:扫描指定的类,用于组件的初始化。

proxyBeanMethods:指定是否代理@Bean方法以强制执行 bean 的声明周期行为。此功能需要通过运行时生成CGLIB子类来实现方法拦截,该子类有一定的限制,比如配置类及其方法不允许声明为 final 等。默认值为true,允许配置类中进行“inter-bean references”(bean之间的引用)以及对该配置的@Bean方法的外部调用。如果@Bean方法都是自包含的,并且仅提供了容器使用的普通工程方法的功能,则可设置为false,避免处理CGLIB子类。Spring Boot 2.2新增该成员属性,自动配置类中基本都会使用,一般情况下都配置为false。

以上源代码会发现,大量使用了@AliasFor注解,该注解用于桥接到其他注解,该注解的属性中指定了所桥接的注解类。如果点进去查看,会发现@SpringBootApplication定义的属性,在其他注解中已经定义过了。之所以使用@AliasFor注解并重新在@SpringBootApplication中定义,更多的意义是为了减少用户使用多注解带来的麻烦。

@SpringBootApplication注解中组合了@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan。因此,在实践过程中也可以使用这三个注解来替代@SpringBootApplication

在早期版本中并没有@SpringBootConfiguration注解,后新增了@SpringBootConfiguration并在其内组合了@Configuration@EnableAutoConfiguration注解组合了@AutoConfigurationPackage。忽略掉一些基础注解和元注解,@SpringBootApplication注解的组合结构可以参考下图。

@SpringBootApplication注解组合结构

在上图中,@SpringBootApplication除了组合元注解之外,核心包括:用于激活Spring Boot自动配置的@EnableAutoConfiguration、用于激活@Component扫描的@ComponentScan、用于激活配置类的@Configuration。其@ComponentScan注解和@Configuration注解在Spring的日常使用中经常用到,不再赘述,而关于@EnableAutoConfiguration正是我们下面要详细介绍的。

1.2.2 注解@EnableAutoConfiguration功能解析

在未使用Spring Boot的情况下,Bean的生命周期由Spring来管理,然而Spring无法自动配置@Configuration注解的类。而Spring Boot所做的核心之一就是根据约定可以自动化的管理该注解标注的类。用来实现该功能的组件之一便是@EnableAutoConfiguration注解。

@EnableAutoConfiguration位于spring-boot-autoconfigure包内,当使用@SpringBootApplication注解时,该注解会自动生效。

@EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置你可能需要的Bean。自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中。

举个例子:如果将tomcat-embedded.jar添加到classpath下,那么@EnableAutoConfiguration会认为你准备使用TomcatServletWebServerFactory类,并帮你初始化相关配置。与此同时如果自定义了基于ServletWebServerFactory的 Bean,那么,它将不会进行TomcatServletWebServerFactory类的初始化。这一系列的操作判断,都由 Spring Boot 来完成。

下面我们来看一下@EnableAutoConfiguration注解的源代码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 用来覆盖配置来开启/关闭自动配置的功能
   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 根据类(Class)排除指定的自动配置
   Class[] exclude() default {};
// 根据类名排除指定的自动配置
   String[] excludeName() default {};
}

该注解提供了一个常量和两个成员参数的定义:

ENABLED_OVERRIDE_PROPERTY:可以用来覆盖配置来开启/关闭自动配置的功能。

exclude:根据类(Class)排除指定的自动配置

excludeName:根据类名排除指定的自动配置。

正如上面所说,@EnableAutoConfiguration会猜测你所需要使用的Bean,但如果在实战中你并不需要它预置初始化的Bean,则可通过该注解的excludeexcludeName参数来进行有针对性的排除。

比如,当不需要数据库的自动配置时,可通过以下两种方式让其自动配置失效:

// 通过@SpringBootApplication排除DataSourceAutoConfiguration
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 
public class SpringLearnApplication {
}

或:

// 通过@EnableAutoConfiguration排除DataSourceAutoConfiguration
@Configuration
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
public class DemoConfiguration {
}

需要注意的是,被@EnableAutoConfiguration注解的类所在 package 还具有特定的意义,通常会被作为扫描注解@Entity的根路径。这也是为什么在使用@SpringBootApplication注解时需要将被注解的类放在顶级 package 下的原因,如果放在较低层级,则它所在 package 的同级或上级中的类则无法被扫描到。

而对于入口类和其main方法来说,并不依赖@SpringBootApplication注解或@EnableAutoConfiguration,也就是说该注解可以使用在其他类上,而非入口类上,同样生效。

1.3 AutoConfigurationImportSelector源码解析

@EnableAutoConfiguration的关键功能是通过@Import注解导入的ImportSelector来完成的。从源码得知@Import(AutoConfigurationImportSelector.class)@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者。

它又可以分为两部分:

@Import和对应的ImportSelector。本节重点讲解@Import的基本使用和ImportSelector的实现类AutoConfigurationImportSelector

1.3.3.AutoConfigurationImportSelector功能概述

下面,通过一张流程图来从整体上了解 AutoConfigurationImportSelector 功能及流程图。

图中省略了外部通过@Import注解调用该类的部分。

AutoConfigurationImportSelector核心功能及流程图

当 AutoConfigurationImportSelector 被 @Import 注解引入之后,它的 selectImports 方法会被调用并执行其实现的自动装配逻辑。读者朋友需注意的 selectImports 方法几乎涵盖了组件自动装配的所有处理逻辑。

通过上图从整体上了解了 AutoConfigurationImportSelector 的概况及操作流程,关于 AutoConfigurationImportSelector 的详细实现可参考Spring Boot中的源码进行逐步分析,本篇文章就不再进行过多拓展。

1.4 小结

本篇文章围绕 Spring Boot 的核心功能展开,带大家从整体上了解 Spring Boot 自动配置的原理以及自动配置核心组件的运作过程。只有掌握了这些基础的组建及其功能,在后续集成其他三方类库的自动配置时才能够更加清晰的了解它们都运用了自动配置的哪些功能。

以上摘自朱智胜《Spring Boot技术内幕——架构设计与实现原理》。


送书福利!!

为了感谢大家一直以来对武培轩的支持,此次我准备了3本《Spring Boot技术内幕——架构设计与实现原理》送给大家~



参与方式


?方式一:公众号【武培轩】回复【送书】,赠书两本

?方式二:到截止日期为止,分享榜第一名,赠书一本

特别提醒:方式一、二可同时参与(获奖几率更大~),活动截止日期8月12日下午6点


获奖公布


公布时间:8月13日次条

特别提醒:兑奖截止至8月20日,请参与读者及时兑奖


       
   
Spring Boot 集成 Redis 实现数据缓存
读 Spring 源码,我们可以从第一行读起
30 张图手把手教你玩转 Spring 编译

觉得不错,点个在看~

浏览 14
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报