SpringBoot原理-SpringBoot核心运行原理

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

文章目录

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

核心运行原理

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

SpringBoot原理-SpringBoot核心运行原理_第1张图片
  上图描述了再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注解来引入@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.
	 * 

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 */ @Nullable default 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的功能
SpringBoot原理-SpringBoot核心运行原理_第2张图片
  当AutoConfigurationImportSelector被@Import注解引入之后,它的selectImports方法会被调用并执行实现了自动装配,需要注意的是selectImports方法几乎是覆盖了所有组件的自动装配处理逻辑。AutoConfigurationImportSelector的selectImports方法源码如下

    @Override
	public 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 的基本流程,后面的的分享中对这些流程进行拆分。

你可能感兴趣的:(Spring,Boot核心技术详解,java,spring,boot,spring,mybatis)