您好,欢迎来到意榕旅游网。
搜索
您的当前位置:首页springfactories机制

springfactories机制

来源:意榕旅游网
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> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) { return result; }

try {

Enumeration urls = (classLoader != null ?

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement();

UrlResource resource = new UrlResource(url);

Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry entry : properties.entrySet()) {

String factoryClassName = ((String) entry.getKey()).trim();

for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } }

cache.put(classLoader, result); return result; }

catch (IOException ex) {

throw new IllegalArgumentException(\"Unable to load factories from location [\" + FACTORIES_RESOURCE_LOCATION + \"]\ } }

从代码中可以看到,在这个⽅法中会遍历整个ClassLoader 中所有Jar包下的spring.factories⽂件,也就是我们可以在⾃⼰jar中配置spring.factories⽂件,不会影响到其他地⽅的配置,也不回被别⼈的配置覆盖。

spring.factories的是通过Properties解析得到的,所以我们在写⽂件中的内容都是按照下⾯这种⽅式配置的。

com.xxx.interface=com.xxx.classname

如果⼀个接⼝希望配置多个实现类,可以⽤\分割。

spring-boot包中的spring.factories⽂件

在Spring Boot 的很多包中都能够找到spring.factories,下⾯就是spring-boot 包中的spring.factories⽂件

# PropertySource Loaders

org.springframework.boot.env.PropertySourceLoader=\\

org.springframework.boot.env.PropertiesPropertySourceLoader,\\org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners

org.springframework.boot.SpringApplicationRunListener=\\

org.springframework.boot.context.event.EventPublishingRunListener# Error Reporters

org.springframework.boot.SpringBootExceptionReporter=\\org.springframework.boot.diagnostics.FailureAnalyzers

# 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# Application Listeners

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

# Environment Post Processors

org.springframework.boot.env.EnvironmentPostProcessor=\\

org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\\org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\\

org.springframework.boot.env.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指定的值,将这些值作为⾃动配置类导⼊到容器中,⾃动配置类就⽣效,帮我们进⾏⾃动配置⼯作。以前我们需要⾃⼰配置的东西,⾃动配置类都帮我们完成了。

J2EE的整体整合解决⽅案和⾃动配置都在spring-boot-autoconfigure-2.0.3.RELEASE.jar:

⽐如看看WebMvcAutoConfiguration:

都已经帮我们配置好了,我们不⽤再单独配置了:

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- yrrf.cn 版权所有 赣ICP备2024042794号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务