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 try { Enumeration 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
本站由北京市万商天勤律师事务所王兴未律师提供法律服务