springfactories机制
Spring Boot 中有⼀种⾮常解耦的扩展机制:Spring Factories.这种机制实际上是仿照java中的SPI扩展机制实现的。
什么是SPI 机制
SPI 的全名为 Service Provider Interface.⼤多数开发⼈员可能不熟悉,因为这个是是针对⼚商或者插件的。在java.util.ServiceLoader 的⽂档⾥有⽐较详细的介绍。
简单总结下Java SPI机制的思想。我们系统⾥抽象的各个模块,往往有很多不同的实现⽅案,⽐如⽇志模块的⽅案,xml解析模块、jdbc模块的⽅案等。⾯向的对象设计⾥,我们⼀般推荐模块之间基于接⼝编程,模块之间不对实现类进⾏硬编码。⼀旦代码⾥涉及了具体的实现类,就违反了可插拔的原则,如果需要替换⼀种实现,就需要修改代码。为了实现在模块装配的时候能不在程序⾥动态指明,这就需要⼀种服务发现机制。
Java SPI 就是提供这样的⼀种机制:为某个接⼝寻服务的实现的机制,有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制很重要。Spring Boot 中的SPI 机制
在Spring boot 中也有⼀种类似的加载机制,它在META-INFO/spring.factories⽂件中配置接⼝的实现类名
称,然后在程序中读取这些配置⽂件并实例化。
这种⾃定义的SPI 机制就是Spring Boot Starter 实现的基础。
Spring Factories实现原理
spring -core 包⾥定义了SpringFactoriesLoader 类,这个类实现了检索META-INF/spring.factories⽂件,并获取指定接⼝的配置的功能。在这个类中定义了两个对外的⽅法:loadFactories 根据接⼝类获取其实现类的实例,这个⽅法返回的是对象列表
loadFactoryNames 根据接⼝获取其接⼝类的名称,这个⽅法返回的是类名的列表。
上⾯两个⽅法的关键都是从指定的ClassLoader中获取spring.factories⽂件,并解析得到类名列表,具体代码如下
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = Element();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : Set()) {
String factoryClassName = ((String) Key()).trim();
for (String factoryName : StringUtilsmaDelimitedListToStringArray((String) Value())) {
result.add(factoryClassName, im());
}
}
springmvc选择题}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
从代码中可以看到,在这个⽅法中会遍历整个ClassLoader 中所有Jar包下的spring.factories⽂件,也就是我们可以在⾃⼰jar中配置spring.factories⽂件,不会影响到其他地⽅的配置,也不回被别⼈的配置覆盖。
spring.factories的是通过Properties解析得到的,所以我们在写⽂件中的内容都是按照下⾯这种⽅式配置的。
如果⼀个接⼝希望配置多个实现类,可以⽤","分割。
spring-boot包中的spring.factories⽂件
在Spring Boot 的很多包中都能够到spring.factories,下⾯就是spring-boot 包中的spring.factories⽂件
# PropertySource Loaders
org.v.PropertySourceLoader=\
org.v.PropertiesPropertySourceLoader,\
org.v.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.t.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
t.ApplicationContextInitializer=\
org.t.ConfigurationWarningsApplicationContextInitializer,\
org.t.ContextIdApplicationContextInitializer,\
org.t.config.DelegatingApplicationContextInitializer,\
org.springframework.t.ServerPortInfoApplicationContextInitializer
# Application Listeners
t.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.t.FileEncodingApplicationListener,\
org.t.config.AnsiOutputApplicationListener,\
org.t.config.ConfigFileApplicationListener,\
org.t.config.DelegatingApplicationListener,\
org.t.logging.ClasspathLoggingApplicationListener,\
org.t.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.v.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.v.SpringApplicationJsonEnvironmentPostProcessor,\
org.v.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
在⽇常⼯作中,我们可能需要实现⼀些SDK 或者Sring boot starter 给别⼈⽤的时候,我们就可以使⽤Factories机制,Factories机制可以让SDK或者Stater的使⽤只需要很少或者不需要进⾏配置,只需要在服务中引⼊我们的Jar包就即可。
⾃动装配
在java spring cloud项⽬中,我们常常会在⼦模块中创建公共类库,作为驱动包。那么在另外⼀个⼦模块中,需要加载配置⽂件的时候,往往Spring Boot ⾃动扫描包的时候,只会扫描⾃⼰模块下的类。
1、引⼊META-INF/spring.factories⽂件
如何让我们的starter⾥的@Configuration在使⽤者的项⽬⾥⽣效呢?
在SpringBoot的启动类中,@SpringBootApplication注解的代码⾥⾯有⼀句@EnableAutoConfiguration
@EnableAutoConfiguration注解加载的是资源⽬录META-INF⽂件下的spring.factories的⽂件。包括导⼊到项⽬中的Jar包的META-INF⽂件夹下的spring.factories⽂件。spring.factories⽂件是⼀个properties⽂件。
2、@ComponentScan注解
作⽤是扫描@SpringBootApplication所在的Application类(即spring-boot项⽬的⼊⼝类)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)标记的bean,并注册到spring容器中。
源码解析
先看看SpringBoot的主配置类:
⾥⾯有⼀个main⽅法运⾏了⼀个run()⽅法,在run⽅法中必须要传⼊⼀个被@SpringBootApplication注解的类。
@SpringBootApplication
SpringBoot应⽤标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就会运⾏这个类的main⽅法来启动SpringBoot项⽬。
那@SpringBootApplication注解到底是什么呢,点进去看看:
发现@@SpringBootApplication是⼀个组合注解。
@SpringBootConfiguration
先看看@SpringBootConfiguration注解:
这个注解很简单,表名该类是⼀个Spring的配置类。
再进去看看@Configuration:
说明Spring的配置类也是Spring的⼀个组件。
@EnableAutoConfiguration
这个注解是开启⾃动配置的功能。
先看看@AutoConfigurationPackage注解:
这个注解是⾃动配置包,主要是使⽤的@Import来给Spring容器中导⼊⼀个组件,这⾥导⼊的是Registrar.class。来看下这个Registrar:
就是通过这个⽅法获取扫描的包路径,可以debug看看:
在这⾏代码上打了⼀个断点:
启动项⽬:
进⼊断点处:
看看能否获取扫描的包路径:
已经获取到了包路径:
那那个metadata是什么呢:
可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类:
说⽩了就是将主配置类(即@SpringBootApplication标注的类)的所在包及⼦包⾥⾯所有组件扫描加载到Spring容器。所以包名⼀定要注意。现在包扫描路径获取到了,那具体加载哪些组件呢,看看下⾯这个注解。
@Import({AutoConfigurationImportSelector.class})
@Import注解就是给Spring容器中导⼊⼀些组件,这⾥传⼊了⼀个组件的选择器:AutoConfigurationImportSelector。
⾥⾯有⼀个selectImports⽅法,将所有需要导⼊的组件以全类名的⽅式返回;这些组件就会被添加到容器中。
debug运⾏看看:
会给容器中导⼊⾮常多的⾃动配置类(xxxAutoConfiguration);就是给容器中导⼊这个场景需要的所有组件,并配置好这些组件:
有了⾃动配置类,免去了我们⼿动编写配置注⼊功能组件等的⼯作。
那他是如何获取到这些配置类的呢,看看上⾯这个⽅法:
会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为⾃动配置类导⼊到容器中,⾃动配置类就⽣效,帮我们进⾏⾃动配置⼯作。以前我们需要⾃⼰配置的东西,⾃动配置类都帮我们完成了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论