SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。Spring的自动化配置功能,也与此息息相关。本文有一部分会以spring整合redis为示例,讲述SpringFactoriesLoader在Spring自动化配置中的作用。
版本: 2.0.6.RELEASE
SpringBoot启动简要流程图
应用初始化ApplicationContextInitializer
Springboot启动过程中第一次调用SpringFactoriesLoader类是为了初始化SpringApplication的initializers成员变量(ApplicationContextInitializer类型)。
//构造SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//通过SpringFactoriesLoader获取ApplicationContextInitializer实现类并设置为成员变量
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication构造函数中通过getSpringFactoriesInstances(ApplicationContextInitializer.class)) 来获取ApplicationContextInitializer实现类并设置为成员变量initializers。方法实现如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//type为ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
//获取ApplicationContextInitializer的实现类,springboot最简配置下应该是6个
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通过默认构造函数创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//通过指定排序器进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这6个实现类,4个在spring-boot-2.0.6.RELEASE.jar包下spring.factories文件中
#Application Context Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
2个在spring-boot-autoconfigure-2.0.6.RELEASE.jar包下的spring.factories文件中
Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
应用初始化ApplicationListener
SpringApplication构造函数中,初始化完ApplicationContextInitializer后,立马就会初始化ApplicationListener。方式与上面介绍的一模一样,这里不再说明。springboot最小配置模式下,共10个实现类。
org.springframework.context.ApplicationListener=
org.springframework.boot.ClearCachesApplicationListener,
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.config.ConfigFileApplicationListener,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
获取SpringApplicationRunListener
SpringApplication的run()方法中获取SpringApplicationRunListener是通过SpringFactoriesLoader来获取的,方式与上面类似。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//通过SpringFactoriesLoader来获取META-INF/spring.factories的实现类
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
正如在第一篇文章中所说,springboot最简配置下,只有一个实现类
#Run Listeners
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
该类在springboot启动的不同阶段发布不同的事件,使ApplicationListener作出响应,就像一个事件响应驱动器一样。
准备ConfigurableEnvironment
public ConfigurableApplicationContext run(String... args) {
...
//通过SpringFactoriesLoader来获取META-INF/spring.factories的实现类
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
...
准备环境过程中,会去寻找org.springframework.boot.env.EnvironmentPostProcessor接口的实现类,
META-INF/spring.factories文件中有三个。
#Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
代码执行过程中,还会多一个org.springframework.boot.context.config.ConfigFileApplicationListener
它既是一个ApplicationListener,又是一个EnvironmentPostProcessor。它包含一个内部类Loader
,这个类在构造过程中也需要获取 org.springframework.boot.env.PropertySourceLoader接口的实现类。在META-INF/spring.factories
文件中是
#PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader
顺带说一下,ConfigFileApplicationListener跟应用的配置文件息息相关,后面的文章会讲述。
准备SpringBootExceptionReporter
public ConfigurableApplicationContext run(String... args) {
...
try {
...
context = createApplicationContext();
//准备启动异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
...
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
...
return context;
}
涉及到org.springframework.boot.SpringBootExceptionReporter接口的子类获取及初始化,以及org.springframework.boot.diagnostics.FailureAnalyzer接口的子类及初始化。
刷新上下文AnnotationConfigApplicationContext
SpringApplication类的refresh方法,是创建上下文context的关键步骤
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
在这个过程中,SpringFactoriesLoader获取了如下接口的实现类
BeanInfoFactory
EnableAutoConfiguration
AutoConfigurationImportFilter
AutoConfigurationImportListener
RepositoryFactorySupport
->invokeBeanFactoryPostProcessors(beanFactory)
->ConfigurationClassPostProcessor.processConfigBeanDefinitions
->SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)
->AutoConfigurationImportSelector.selectImports()
->SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)
这个基本过程是ConfigurationClassPostProcessor这个Configuration Bean工厂后置器去扫描要注册的bean。AutoConfigurationImportSelector的selectImports方法返回的一组类应该被当做bean注册到容器中,这个方法其实最终是到META-INF/spring.factories
文件中找EnableAutoConfiguration
为键的值(一组类),RedisAutoConfiguration
只是其中的一个值。
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisAutoConfiguration将被标记且注册为bean,其上的@Import注解中的
LettuceConnectionConfiguration也会被标记为bean进行注册,而
JedisConnectionConfiguration不会被标记为bean,因为默认情况下类路径下没有其依赖的Jedis.class
@EnableConfigurationProperties(RedisProperties.class)会使RedisProperties类也被标记为bean。从RedisProperties类的定义可以看出它仅仅是一个配置映射类,配置项都是以spring.redis开头。配置文件中,一定要配置redis数据源
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
...
}
总结
SpringFactoriesLoader在SpringBoot启动流程中的作用与META-INF/spring.factories
文件关系密切,该文件中的工厂实现类,几乎都要被SpringFactoriesLoader标记注册为bean并实例化。比如与SpringBoot启动周期阶段相关的ApplicationContextInitializer、ApplicationListener、SpringApplicationRunListener、SpringApplicationRunListener、EnableAutoConfiguration等类。
SpringBoot的自动化配置重度依赖@EnableAutoConfiguration注解、SpringFactoriesLoader、META-INF/spring.factories等重要要素。
相关博客
springboot启动流程简析
Springboot内置ApplicationListener–ConfigFileApplicationListener
SpringBoot实战之SpringBoot自动配置原理
Spring ConfigurationClassPostProcessor Bean解析及自注册过程
@EnableAutoConfiguration注解原理