这是我参与更文挑战的第一天,活动详情查看:更文挑战
前言
SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,与java spi类似,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。
下面以SpirngBoot的自动配置@EnableAutoConfiguration(springboot 2.0.x版本)的实现来讲解SpringFactoriesLoader。
可以知道,该配置已经是满足了SpringFactoriesLoader的要求,下面我们来看看是如何来触发其找到相应的类并加载的。
可以看到在SpringBootApplication中有@EnableAutoConfiguration注解;
可以看到该注解,@Import(AutoConfigurationImportSelector.class)该注解会将AutoConfigurationImportSelector实例化并注入容器中。
AutoConfigurationImportSelector中selectImports方法
selectImports方法的核心代码
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
复制代码
一起看一下getAutoConfigurationEntry方法:
getAutoConfigurationEntry方法的核心代码
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
复制代码
getCandidateConfigurations方法如下图:
可知,这里终于调用了SpringFactoriesLoader的方法:
List configurations =SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
复制代码
getSpringFactoriesLoaderFactoryClass()返回的是EnableAutoConfiguration.class;
SpringFactoriesLoader.loadFactoryNames()方法:
这里 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
可知,这个在便利所用的jar包下的META-INF/spring.factories文件,并对相应的key值进行筛选,这里使用的key值为org.springframework.boot.autoconfigure.EnableAutoConfiguration。
这样,我们就能得到对应的一组@Configuration类,我们就可以通过反射实例化这些类然后注入到IOC容器中,最后容器里就有了一系列标注了@Configuration的JavaConfig形式的配置类。
例如Tomcat容器的加载,在本文开头的spring.factories配置文件中,在key为EnableAutoConfiguration中有如下配置类:
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
复制代码
EmbeddedWebServerFactoryCustomizerAutoConfiguration类:
/**
* Nested configuration if Tomcat is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
public static class JettyWebServerFactoryCustomizerConfiguration {
@Bean
public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
public static class UndertowWebServerFactoryCustomizerConfiguration {
@Bean
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
/**
* Nested configuration if Netty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpServer.class)
public static class NettyWebServerFactoryCustomizerConfiguration {
@Bean
public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
复制代码
可以看到该配置类中是对springboot2.0中支持四种web容器,关于载入哪种是根据其是否依赖了相应的容器实现类(@ConditionalOnClass控制实现)。
在springboot中默认是使用Tomcat做为web容器(springboot2.0.x),因为
该依赖默认会导入Tomcat的jar包。
在springboot中开发了大量的spring-boot-starter的组件,组件中依赖了相应的实现包。
总结
使用SpringFactoriesLoader寻找jar包配置META-INF下的spring.fatories配置文件相应key的value类,然后通过spring的@Configuration对相应的bean进行有选择(@ConditionalOnClass)的实例化。