导语
  Spring Boot最为核心的功能就是自动配置,所有功能的实现都是基于“约定优于配置”的原则,但是Spring Boot是如何实现自动配置的功能的,下面就通过源码学习Spring Boot的核心运作原理,内容包括自动配置的运作原理、核心功能模块、核心注解以及使用到的核心代码分析等等的内容。

文章目录

  • 核心运行原理
  • 运作原理源码解析之@EnableAutoConfiguration
    • 入口类和@EnableAutoConfiguration注解
    • 注解@EnableAutoConfiguration功能解析
  • AutoConfigurationImportSelector源码解析
    • @Import 注解
    • ImportSelector接口
    • AutoConfigurationImportSelector功能概述

核心运行原理

  使用Spring Boot的时候,通过引入对应场景的Starter,被称为场景启动器,再Spring Boot项目启动的时候会自动加载相关的依赖,添加相应的默认配置。通过最简单的方式来完成对于第三方组件的集成操作,这个就是Spring Boot自动配置的核心的思想。如下图所示,图例来源某电子书截图


  上图描述了再Spring Boot自动配置功能实现的过程中涉及到的几个核心实现功能之间的逻辑关系图。主要包括的就是@EnableAutoConfiguration注解,spring.factories配置 自动配置的条件判断,以及Starter场景启动器的其他依赖配置。
  Spring Boot通过@EnableAutoConfiguration注解来开启自动配置,通过加载spring.factories中配置的配置的自动配置类,当某个AutoConfiguration类满足@Conditional所指定的生效条件的时候,就可以实例化对应的的AutoConfiguration类中所定义的@Bean组件,并且注入到容器中,这样就可以再整个的项目中使用对应的功能了。也就完成了自动配置。
  下面来分开说明一下对应的内容

  • @EnableAutoConfiguration;该注解由组合注解@SpringBootApplication引入,完成自动配置开启,扫描各个包下的spring.factories文件,并加载文件中注入的自动配置类,当然自定义的场景启动器也是通过这种方式进行加载。
  • spring.factories;配置文件,位于jar包的META-INF目录下,按照指定格式注册了自动配置的AutoConfiguration类,spring.factories也可以包含其他类型的需要注册的类。该文件不仅存在于Spring Boot的项目中,当然也可以存在于自定义的场景启动器Starter中。
  • AutoConfiguration类;自动配置类,代表了Spring Boot 中的一类配置类,再这些配置类中定义了第三方需要使用的Bean的初始化以及初始化的条件。
  • @Conditional;条件注解,这个是Spring提供的注解,通过这个注解也衍生了很多的其他注解。表示满足定义的条件的时候才会被实例化成Spring 容器中的Bean对象。
  • Starter;场景启动器,第三方组件的自动配置依赖启动器,默认将满足某一使用场景的第三方配置全部包含到其中,再使用的时候直接引用对应的starter就可以实现对于某个场景的快速实现。

运作原理源码解析之@EnableAutoConfiguration

  @EnableAutoConfiguration是开启自动配置的注解,在创建的Spring Boot项目中并不能直接看到这个注解,它是通过组合注解@SpringBootApplication引入的。所以要想了解这个注解需要先对@SpringBootApplication注解来进行了解一下,然后再深入的了解@EnableAutoConfiguration注解的作用。

入口类和@EnableAutoConfiguration注解

  Spring Boot项目需要创建一个启动类,这个启动类上面需要标注一个注解@SpringBootApplication,表示启用了SpringBoot自动配置方式。

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

  这里main方法没有其他的意思,就是再程序启动的时候需要一个主入口文件,这个是每个程序启动必须的内容。会看到这个类上就标注了@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 {};/*** 配置基础注解扫描包,扫描这个包中的所有的组件,并且将组件注入到容器中,* 这里需要注意的就是,如果不指定,这个默认的包是标注了@SpringBootApplication所在的包* 的所有子包。*/@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")String[] scanBasePackages() default {};/*** 跟上面的内容类似,扫描指定的类,并且注入到容器中*/@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};/*** BeanNameGenerator 类是用来通过命名方式来检查注入到容器中的组件的。当然还提供了通过AnnotationBeanNameGenerator的方式来* 进行检测,这里需要说明一点是,再Spring基础中容器中注入的组件标识都是默认就是对应的组件类名小写。*/@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;/*** 指定是否代理@Bean方法以强制执行Bean的生命周期行为*/@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}

  通过以上的代码会看到,在上面代码中使用了很多的@AliasFor注解,这个注解的作用是用于桥接到其他的注解,这个注解的属性中指定的了所要桥接的注解类。会发现在@SpringBootApplication注解定义的属性在其他的注解中已经定义过了,之所以使用这个注解,并且重新在@SpringBootApplication中定义,是为了减少使用多个注解带来的麻烦。

  @SpringBootApplication注解中组合了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。所以,在实际使用的过程中也可以使用这三个注解来代替@SpringBootApplication。

  在早期的SpringBoot版本中并没有@SpringBootConfiguration注解,在版本升级之后增加了@SpringBootConfiguration 并在其中组合了@Configuration。@EnableAutoConfiguration注解组合了@AutoConfigurationPackage。

  如上图所示,@SpringBootApplication除了组合元注解之外,它的核心作用包括:激活Spring Boot自动配置、激活包扫描的注解类、激活配置类的注解@Configuration。
  在这些注解中,@ComponentScan注解和@Configuration注解在日常使用Spring的时候也算是Spring的基础注解。所以说就不在多说了。

注解@EnableAutoConfiguration功能解析

  在未使用Spring Boot的情况下,Bean的生命周期是Spring容器来进行管理的,当然在Spring中没有办法自动配置@Configuration 注解的类。所以在Spring Boot通过约定的方式来进行自动管理标记了@Configuration注解的类。那么如果使用约定的方式来实现自动配置呢?这就出现了@EnableAutoConfiguration注解。
  @EnableAutoConfiguration在spring-boot-autoconfigure包内,在使用了SpringBoot之后,这个注解就自动生效了。主要功能就是再SpringBoot项目启动的时候,对于Spring容器中注入对应的配置类。再SpringBoot中自动配置通常是基于项目classpath中引入的类和已经定义好的Bean来实现的。再这个过程中,就通过配置来引入各个场景启动器中所要使用到的组件,来引入对应的组件到容器中。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {/*** Environment property that can be used to override when auto-configuration is* enabled.* 用来覆盖配置的开启或者关闭*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude* 根据具体的Class进行排除*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0* 根据指定的Bean名称来进行排除*/String[] excludeName() default {};}

  @EnableAutoConfiguration注解会根据不同的使用场景,来注入需要使用的Bean对象。但是在实际的开发中,再某些场景中Spring Boot提供的组件并不是太好,所以需要通过exclude的方式来进行排除,再其他地方进行新的配置。但是需要注意的一点是。被@EnableAutoConfiguration注解的类所在的package具有特定的含义。通常会被作为扫描注解@Entity的根路径。这个就是上面说到的被@SpringBootApplication注解需要将被注解的类放在顶级的package下的原因。如果放入的等级较低,则无法扫描到高级package中的内容。

AutoConfigurationImportSelector源码解析

  @EnableAutoConfiguration 的关键的功能就是通过@Import 注解导入第三方的组件。查看源码可以知道@Import(AutoConfigurationImportSelector.class) ,这个是自动配置功能的核心功能。这两个注解可以分为两部分,一部分是@Import 一部分是后面的选择器。

@Import 注解

  @Import注解是Spring 提供的注解,主要是通过外部导入的方式来导入配置类。再Spring Boot中使用了大量的EnableXXX的类,通过了解@Import注解的用法来帮助理解Spring Boot自动配置原理。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {/*** {@link Configuration @Configuration}, {@link ImportSelector},* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.*/Class<?>[] value();}

  @Import注解的作用和xml配置的<import/>标签的作用一样,可以通过@Import注解来引入@Configuration注解的类,也可以导入实现了ImportSelector或者是ImportBeanDefinitionRegistrar的类,还可以通过该注解导入普通的POJO。

ImportSelector接口

  @Import的许多功能都需要借助接口ImportSelector 来实现,ImportSelector 决定可以引入那些@Configuration。ImportSelector接口源码如下

public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.* @return the class names, or an empty array if none*/String[] selectImports(AnnotationMetadata importingClassMetadata);/*** Return a predicate for excluding classes from the import candidates, to be* transitively applied to all classes found through this selector's imports.* <p>If this predicate returns {@code true} for a given fully-qualified* class name, said class will not be considered as an imported configuration* class, bypassing class file loading as well as metadata introspection.* @return the filter predicate for fully-qualified candidate class names* of transitively imported configuration classes, or {@code null} if none* @since 5.2.4*/@Nullabledefault Predicate<String> getExclusionFilter() {return null;}}

  ImportSelector 提供了一个参数为AnnotationMetadata 的方法,返回的结果是一个字符串数组。其中参数AnnotationMetadata 内包含了被@Import注解的类的注解信息。在selectImports方法内可根据具体的实现决定返回哪些配置类的全限定名,将获取到的结果以字符串的形式,通过字符串数组的方式返回。

  如果实现了ImportSelector的类的同时,也实现以下的四个接口;

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

  在AutoConfigurationImportSelector 的源码中就实现了上面四个接口。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();private static final String[] NO_IMPORTS = {};private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";private ConfigurableListableBeanFactory beanFactory;private Environment environment;private ClassLoader beanClassLoader;private ResourceLoader resourceLoader;private ConfigurationClassFilter configurationClassFilter;

  通过上面的代码,发现AutoConfigurationImportSelector 并没有直接实现ImportSelector接口,而是实现了它的子接口DeferredImportSelector。DeferredImportSelector接口于ImportSelector的区别是,前者会在所有的@Configuration类加载完成之后再加载返回的配置类,而ImportSelector是在加载完成@Configuration类之前先去加载返回的配置类。

  DeferredImportSelector 的加载顺序可以通过@Order注解或者是Ordered接口来进行指定。与此同时,DeferredImportSelector提供了新的方法getImportGroup() 来跨DeferredImportSelector 实现自定义Configuration的加载顺序。

AutoConfigurationImportSelector功能概述

  通过下面流程图来看一下AutoConfigurationImportSelector的功能

  当AutoConfigurationImportSelector被@Import注解引入之后,它的selectImports方法会被调用并执行实现了自动装配,需要注意的是selectImports方法几乎是覆盖了所有组件的自动装配处理逻辑。AutoConfigurationImportSelector的selectImports方法源码如下

    @Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 检查自动配置功能是否启动,默认是开启的状态if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 加载自动配置的元信息,配置文件中再META-INF目录下面的内容AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 返回符合条件的配置类的全类名return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);// 通过SpringFactoriesLoader类提供的方法加载类路径中META-INF目录下的// spring.factories 文件中针对EnableAutoConfiguration的注册配置类    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 对获得的注册配置类集合进行去重处理,防止多个项目引入同样的配置类。configurations = removeDuplicates(configurations);// 获得 注解中 被exclude 或者 excludeName 所排除的类的集合Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 检查被排除的类是否可实例化,是否被自动注册配置所使用,不符合条件则抛出异常checkExcludedClasses(configurations, exclusions);// 从自动配置类集合中去除被排除的类configurations.removeAll(exclusions);//检查配置类的注解是否符合spring.factories 文件中AutoConfigurationImportFilter指定的注解检查条件configurations = getConfigurationClassFilter().filter(configurations);// 将筛选完成的配置类和排查的配置类构建为事件类,并传入监听器,监听器配置在spring.factories文件中,通过AutoConfigurationImportListenter指定fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}

  通过上面的代码,从整体上了解了AutoConfigurationImportSelector 的基本流程,后面的的分享中对这些流程进行拆分。

SpringBoot原理-SpringBoot核心运行原理相关推荐

  1. 深入解析SpringBoot核心运行原理和运作原理源码

    SpringBoot核心运行原理 Spring Boot 最核心的功能就是自动配置,第 1 章中我们已经提到,功能的实现都是基于"约定优于配置"的原则.那么 Spring Boot ...

  2. Java线性池的使用方式以及核心运行原理

    Java线性池的使用方式以及核心运行原理 一.为什么需要线性池? 二.线性池的处理流程 三.线程池的使用(ThreadPoolExecutor) 四.线程池的注意事项 一.为什么需要线性池? java ...

  3. java能够运行的原理_JAVA程序运行原理分析(一)

    作为JAVA的开发人员,需要知道JAVA是如何运行的,这个需要好好思考下. (一)class文件内容 class文件包含JAVA程序执行的字节码,也就是说程序的执行是通过class里面的内容进行执行的 ...

  4. libuv 原理_Nodejs的运行原理-libuv篇

    前言 这应该是Nodejs的运行原理的第7篇分享,这篇过后,短时间内不会再分享Nodejs的运行原理,会停更一段时间,PS:不是不更,而是会开挖新的坑,最近有在研究RPG Maker MV,区块链,云 ...

  5. python解析器原理_Python程序运行原理图文解析

    本文研究的主要是Python程序运行原理,具体介绍如下. 编译型语言(C语言为例) 动态型语言 一个程序是如何运行起来的?比如下面的代码 #othermodule.py def add(a, b): ...

  6. springBoot学习(二) 基础运行原理

    结论:SpringBoot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应 ...

  7. Java多线程系列(三):Java线程池的使用方式,及核心运行原理

    之前谈过多线程相关的4种常用Java线程锁的特点,性能比较.使用场景,今天主要分享线程池相关的内容,这些都是属于Java面试的必考点. 为什么需要线程池 java中为了提高并发度,可以使用多线程共同执 ...

  8. linkedblockingqueue使用_Java线程池的使用方式,核心运行原理、以及注意事项

    为什么需要线程池 java中为了提高并发度,可以使用多线程共同执行,但是如果有大量线程短时间之内被创建和销毁,会占用大量的系统时间,影响系统效率. 为了解决上面的问题,java中引入了线程池,可以使创 ...

  9. java的运行原理_Java的运行原理(转载)

    在Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器.这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口.编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由 ...

最新文章

  1. android Butter Knife 使用详解
  2. go语言之行--基础部分
  3. ant使用指南详细入门教程
  4. Linux系统编程:lseek扩展文件大小失败原因分析
  5. android网络请求线程池,利用线程池实现Android客户端的http网络数据请求工具类
  6. 【LeetCode】【HOT】581. 最短无序连续子数组
  7. 介绍Ext JS 4.2的新特性的《深入浅出Ext JS》上市
  8. 软件系统的测试计划,软件系统测试计划-模板
  9. dede后台登陆提示 验证码不正确 解决办法(新版)
  10. vscode自动生成项目目录结构
  11. box-sizing: border-box的作用
  12. 7-6 统计大写辅音字母
  13. 复旦大学数学学院转入大数据学院的14级同学对高等代数课程的评价
  14. ads的designguide打不开报错
  15. ios 凭据验证_苹果内购服务器验证凭证回执Data
  16. 微信公众号url接口配置,使微信公众号更多功能化(python简单解决)
  17. 阿里云域名实名认证操作图文详情 新人必看教程
  18. BLE_BQB Test_Intermodulation Performance, uncoded data at 1 Ms/s_RF-PHY/RCV/BV-05-C
  19. 电脑调试UC浏览器,或者本机浏览器
  20. 2.郝斌C语言课程大纲

热门文章

  1. centos php7 redis,CentOS7 yum快速安装php7.1+nginx+mysql+redis
  2. php制作的ios端 跳转url,ThinkPHP 简易开发思路 MVC和URL跳转
  3. SQL Server SQL高级查询语句小结(转)
  4. IIS OCIEnvCreate failed with return code -1
  5. 专题实验 字符集(全球化支持)
  6. [置顶] J2EE (八) 策略模式+装饰模式+反射(java)
  7. “产学合作勇创新·协同育人书新篇”贵州理工大数据学院数据科学训练营结题答辩报告会圆满举行...
  8. Linux扩展正则表达式
  9. ROS系统MoveIt玩转双臂机器人系列(二)--生成MoveIt配置包
  10. WebForm页面间传值方法(转)