SpringBoot源码分析(二):spring.factories 文件

spring.factories

文件内容摘选

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider

自动加载机制简介

SpringBoot 的自动加载机制类似于 JavaSPI加载机制(框架面向接口开发,具体实现留给第三方或者扩展实现,框架通过扫描这些实现进行第三方服务或扩展的调用),SpringBoot通过读取在jar包的 META-INF下的spring.factories读取加载类信息。 而spring.factories 实质上是一个 properties 文件。

SpringBoot首先定义了一系列的接口,在SpringBoot启动过程中,调用实现了这些接口的类完成SpringBoot的自动配置。

在spring.factories文件中,以这些接口的类路径为key,以实现了这些接口(EnableAutoConfiguration除外)的实例类列表为value。SpringBoot在程序加载的时候,通过ClassLoader检索到各个Jar包中的 META-INF/spring.factories 文件,然后通过读取配置文件中各个接口的类的列表,实例化这些类,并在SpringBoot启动的时候调用这些 实例类的方法完成第三方SpringBoot库的自动配置。

具体SpringBoot是怎么读取和解析到这些接口实现类的列表可以参考上篇文章 SpringBoot源码分析(一):SpringApplication的构造函数

各接口简介

org.springframework.context.ApplicationContextInitializer

ApplicationContextInitializer是一个接口,其中只有一个方法

/**
 * 初始化应用程序上下文
 */
void initialize(C applicationContext);

作用是在 ConfigurableApplicationContext调用refresh() 之前初始化它。其实就是在容器刷新之前调用这个接口的initialize方法。也就是说实现了这个接口的类可以在SpringBoot加载Bean之前做一些准备工作。而这个ConfigurationApplicationContext我们也非常熟悉,就是SringBoot的上下文对象,我们调用SpringApplication.run()启动SpringBoot的时候返回的就是这个对象。

通常在需要对应用程序上下文进行一些编程初始化的 Web 应用程序中使用。 例如,针对上下文环境注册属性源或激活配置文件。

org.springframework.context.ApplicationListener

这个接口同样也只有一个方法

/**
 * 处理应用事件
 */
void onApplicationEvent(E event);

通过文档可以看到:ApplicationListener接口使用了观察者模式并继承了 java.util.EventListener 接口, 可以通用地声明它感兴趣的事件类型。当使用 Spring ApplicationContext 注册时,事件将被相应地过滤,侦听器只会被调用以匹配事件对象。

也就是说,这个接口可以注册自己感兴趣的事件,当通过SpringApplicationContext注册的时候,不感兴趣的事件将会被过滤掉。而这些事件都是继承自ApplicationEvent的子类,程序可以通过泛型来注册自己想要监听到的事件。

这也就解释了我第一篇文章的疑问,为什么我自己写的EventListener会被调用多次,那是因为我的listener注册的事件是SpringApplicationEvent,而这个Event有好几个事件的实现:ApplicationContextInitializedEvent,ApplicationReadyEvent,ApplicationStartedEvent,这些个事件触发的时候listener都会被调用。

另外还有一个疑问:众所周知,泛型在编译时会有类型擦除问题,也就是说在调用onApplicationEvent的时候,是没有办法知道这个方法注册是到底是那种事件类型的。那么SpringBoot是怎么做到对应的事件调用对应Listener的方法的呢?这个还需要继续看源码才能够确定。

org.springframework.boot.SpringApplicationRunListener

官方文档:SpringApplication run方法的侦听器。SpringApplicationRunListener是通过SpringFactoriesLoader加载的,并且应该声明一个公共构造函数,该构造函数接受一个SpringApplication实例和一个String[]参数。 每次运行都会创建一个新的SpringApplicationRunListener实例。

这个接口的方法有点多,总共有7个,分别对应这SpringBoot加载过程中的各个阶段。不过目前只发现了一个类实现了这个接口,那就是EventPublishingRunListener, 看名字就可以看得出来,我们上面ApplicationListener注册的事件都是通过这个类来进行分发的。所以研究SpringBoot的事件分发机制就需要研究一下这个类是怎么实现的。

这个接口的所有方法如下

  • void starting()

    这个方法会在run方法(SpringApplication#run())刚被调用的时候最先被调用,可以用于最早的初始化。

  • void environmentPrepared(ConfigurableEnvironment environment)

    这个方法在运行环境准备好后执行,在ApplicationContext被调用前被调用。(在创建ApplicationContext之前SpringBoot会先创建运行环境,根据不同的应用类型创建不同的环境,具体有StandardEnvironment,StandardReactiveWebEnvironment,StandardServletEnvironment,分别对应了我们在 SpringBoot源码分析(一):SpringApplication的构造函数 文章中提及的三种应用类型

  • void contextPrepared(ConfigurableApplicationContext context)

    在创建并准备好ApplicationContext 和在加载资源之前调用

  • void contextLoaded(ConfigurableApplicationContext context)

    在加载完成程序上下文并且还没有刷新上下文之间调用

  • void started(ConfigurableApplicationContext context)

    上下文已刷新且应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners

  • void running(ConfigurableApplicationContext context)

    在 run 方法完成之前立即调用,此时应用程序上下文已刷新并且所有CommandLineRunners和ApplicationRunners已被调用

  • void failed(ConfigurableApplicationContext context, Throwable exception)

    在运行应用程序时发生故障时调用, 报错的时候context参数可能为空

org.springframework.boot.autoconfigure.AutoConfigurationImportListener

可以在spring.factories中注册以接收导入的自动配置的详细信息的监听器。这个类同样也是实现了EventListener接口。

这个接口也提供了一个方法void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event), 这个方法用来处理自动装配事件,这个event中包含了两个列表:自动装配的Bean的类列表和被排除的Bean列表。

同事实现了这个接口的类还可以通过通过实现下面的接口方法,而且这些接口的方法将会在onAutoConfigurationImportEvent方法之前调用。

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

这四个接口都继承自org.springframework.beans.factory.Aware接口,而这个接口是一个标记接口,指示 bean 有资格通过回调样式的方法被特定框架对象的 Spring 容器通知。通过上面四个接口以及他们的名称可以知道。他们用于获取对应的SpringBoot创建过程中的对应实例:EnvironmentAware用于获取环境实例,BeanFactoryAware用于获取BeanFactory实例,BeanClassLoaderAware用于获取ClassLoader实例,ResourceLoaderAware用于获取ResourceLoader实例。

那么它们这么配合这个Listener的用途就很明了了,我们可以在自动装配以后,通过这个接口对自动装配的类做一些操作。

例如 AutoConfigurationImportListener有一个实现类ConditionEvaluationReportAutoConfigurationImportListener, 同过获取自动装配列表和排除列表来统计自Condition信息。而获取统计Condition的ConditionEvaluationReport对象需要BeanFactory对象,这时候就需要再实现BeanFactoryAware接口来获取BeanFactory对象。这也就说明了为什么这些个Aware对象的方法的调用顺序要在onAutoConfigurationImportEvent之前了。因为Aware接口中的方法相当于一个对象装配方法,例如初始化某些变量,而onAutoConfigurationImportEvent就是业务处理方法。

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter

这个接口是一个过滤器,同样在spring.factories 中配置其实现类。以限制考虑的自动配置类。 此接口旨在允许在读取字节码之前快速删除自动配置类。

和AutoConfigurationImportListener一样,同样可以通过实现下面这四种接口获取所需的对象,这里就不在赘述。

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

具体来看它的方法

/*
 * 过滤候选的自动配置类
 * @param autoConfigurationClasses 候选的自动配置类,可能为空,在实现这个方法的时候不要修改这个数组中的内容
 * @param autoConfigurationMetadata 这个对象中包含了一些信息,这些信息是在META-INF/spring-autoconfigure-metadata.properties文件中定义的,这个文件存储的是”待自动装配候选类“的过滤条件,这个信息很重要,框架会根据里面的规则逐一对候选类进行计算看是否需要被自动装配进容器
 * @return 一个boolean数组,其元素个数应该和autoConfigurationClasses 一致,表示应该导入那些自动配置类。需要导入的返回true,不需要的返回false
 */
boolean[] match(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata);

也就是说,这个类通过AutoConfigurationMetadata的元素信息或者其他信息来判断自动装配的类是否需要自动装配。

org.springframework.boot.diagnostics.FailureAnalyzer

FailureAnalyzer用于分析故障并提供可显示给用户的诊断信息。

这个接口主要就是用来处理异常的,在SpringBoot启动过程中发生异常的时候,异常会通过这个接口进行分析然后展现给用户。

/**
 * 返回对failure的分析,如果无法分析返回null
 */
FailureAnalysis analyze(Throwable failure); 

这个接口只有一个方法 ,对于异常返回一个FailureAnalysis对象,这个对象包含了异常的描述(description),以及出现了这个异常后对用户的处理建议 (action)

org.springframework.boot.autoconfigure.EnableAutoConfiguration (注解)

这个注解是SpringBoot实现自动配置的核心,SpringBoot会读取这个注解标记下面的所有的自动配置类进行自动配值,值得注意和其他的标记不同的是,EnableAutoConfiguration是一个注解,而不是一个接口。这也就意味着对应于这个标记下面的类的列表不需要去实现什么接口。根据官方的类的文档可以看到,这个注解的作用有:

  1. 启动SpringBoot的上下文自动配置,尝试去推测和配置可能需要的bean。
  2. @SpringBootApplication 注解已经包含了这个注解,所以用@SpringBootApplication注解的时候就已经自动添加了这个注解
  3. exclude() 方法和 excludeName() 方法用来排除Bean,如果不想应用某个bean可以通过这个方法来定义,也可以通过配置 pring.autoconfigure.exclude 来达到相同的效果
  4. SpringBoot会通过被@EnableAutoConfiguration 注解标注的类(一般都是通过@SpringBootApplication标注)所在的package扫描Bean
  5. 在spring.factories 中的org.springframework.boot.autoconfigure.EnableAutoConfiguration标记下的类列表,需要使用 @Configuration 注解标记。

也就是说EnableAutoConfiguration注解有两个作用:

  1. 配置在SpringBoot应用的入口类,作为一个标识扫描SpringBoot项目下的bean
  2. 作为spring.factories 文件中的一个自动配置标签,在这个标签下的类需要使用@Configuration注解进行标记。而这个@Configuration注解我们已经很熟悉了,被@Configuration注解的类就相当于一个配置类,在这个类中配置的@Bean可以被SpringBoot扫描到。如果我们也想自己写一个starter的话,想要我们定义的bean能够被SpringBoot自动扫描到的话,就需要将@Configuration标注的类放在这个标签下面。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值