SpringBoot系列 - SPI机制

SpringBoot系列- SPI机制

   概要

    什么是SPI呢,全称是 Service Provider Interface 。简单翻译的话,就是服务提供者接口,是一种寻找服务实现的机制。这个是针对厂商或者插件的。

    其实就是一个规范定义、或者说是实现的标准。

    一、SPI的核心思想

    SPI的核心思想是模块之间基于接口编程不对实现类进行硬编码增强可拔插性它解决了模块在替换实现时无需修改代码的问题。Java SPI 提供了一种服务发现机制,用于动态寻找某接口的实现,从而在模块装配时无需显式指定具体实现。

   直白一点来说就是:我定义了一个接口,但是不想固定死具体的实现类,因为那样如果要更换实现类就要改动源代码,这往往是不合适的。那么我也可以定义一个规范,在之后需要更换实现类或增加其他实现类时,遵守这个规范,我也可以动态的去发现这些实现类。

   二、Spring Boot中的类SPI扩展机制

   SpringBoot在启动的时候,会扫描所有jar包 resource/META-INF/spring.factories 文件,加载进去,依据类的全限定名利用反射机制将 Bean 装载进容器中

   加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。

   举个例子:META-INF/spring.factories中监听器的配置

   监听器的实现类都是在spring.factories文件中配置好的,代码中通过getSpringFactoriesInstances方法获取,这种机制叫做SPI机制:通过本地的注册发现获取到具体的实现类,轻松可插拔。

   # Run Listeners
   org.springframework.boot.SpringApplicationRunListener=\
   org.springframework.boot.context.event.EventPublishingRunListener

   这行配置表明 EventPublishingRunListenerSpringApplicationRunListener 接口的一个实现类。当 Spring Boot 应用启动时,它会读取 spring.factories 文件,并找到所有实现了 SpringApplicationRunListener 接口的类,并实例化它们

  三、源码分析

  1. 入口方法 getSpringFactoriesInstances()

  根据类型获取META-INF/spring.factories文件中对应的实现类

  SpringApplication构造方法: 

 1     public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 2         this.resourceLoader = resourceLoader;
 3         Assert.notNull(primarySources, "PrimarySources must not be null");
 4         this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
 5         this.webApplicationType = WebApplicationType.deduceFromClasspath();
 6         this.bootstrapRegistryInitializers = new ArrayList<>(
 7                 getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
 8         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 9         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
10         this.mainApplicationClass = deduceMainApplicationClass();
11     }

  SpringApplication#getSpringFactoriesInstances

 1 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
 2     return getSpringFactoriesInstances(type, new Class<?>[]{});
 3 }
 4 
 5 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
 6     ClassLoader classLoader = getClassLoader();
 7     // Use names and ensure unique to protect against duplicates
 8     //从META-INF/spring.factories中加载对应类型的实现类
 9     Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
10     // 加载上来后反射实例化
11     List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
12     //对实例列表进行排序
13     AnnotationAwareOrderComparator.sort(instances);
14     return instances;
15 }

  2. SpringFactoriesLoader

  SpringFactoriesLoader 是 Spring 框架中的一个工具类,用于从类路径下的 META-INF/spring.factories 文件中加载工厂类。

  我们看看它源码(精简):

 1 public final class SpringFactoriesLoader {
 2 
 3     private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
 4 
 5     public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
 6 
 8     //加载spring.factories文件中的Factory名称的
 9     public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
11     }
13 
14     //它只加载 spring.factories 文件中与传入的 factoryClass 对应的条目(即与给定类名匹配的键对应的实现类),并返回这些实现类的实例
15     public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
17     }
18 
19     //从 spring.factories 文件中加载所有的工厂类定义,并以键值对的形式返回。其中 key 是工厂接口或配置类的全限定名,value 是对应的实现类的全限定名列表。
20     private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
21 
22     }
23 }

   1) loadFactoryNames 方法

   在该方法里,首先拿到ClassLoader,然后加载FactoryNames,加载类型(type)为ApplicationContextInitializer,类加载器(classLoader)为刚刚拿到的类加载器,返回值放入一个Set中,为的是确保没有重复的FactoryName,这是因为在之后加载的两个spring.factories配置文件中有两个重复的FactoryName。

   SpringFactoriesLoader#loadFactoryNames

1     public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
2         ClassLoader classLoaderToUse = classLoader;
3         if (classLoaderToUse == null) {
4             classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
5         }
6         String factoryTypeName = factoryType.getName();
7         return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
8     }

​   可以看到,加载的配置文件在META-INF下,名称为spring.factories,该配置文件一共有两个,且配置文件中,每个段落第一行为Key,后边为value,读取时会通过key将所有的value拿出来。 在配置文件中我们发现,key和value都是包名加类名的字符串,因此Springboot在读取文件后,是通过反射生成的类。

   SpringBoot默认情况下提供了两个spring.factories文件,分别是:

  •    spring-boot-2.2.2.RELEASE.jar
  •    spring-boot-autoconfigure-2.2.2.RELEASE.jar

   spring-boot-2.2.2.RELEASE.jar - 该配置文件内容如下:

 1 # PropertySource Loaders
 2 org.springframework.boot.env.PropertySourceLoader=\
 3 org.springframework.boot.env.PropertiesPropertySourceLoader,\
 4 org.springframework.boot.env.YamlPropertySourceLoader
 5 
 6 # Run Listeners
 7 org.springframework.boot.SpringApplicationRunListener=\
 8 org.springframework.boot.context.event.EventPublishingRunListener
 9 
10 # Error Reporters
11 org.springframework.boot.SpringBootExceptionReporter=\
12 org.springframework.boot.diagnostics.FailureAnalyzers
13 
14 # Application Context Initializers
15 org.springframework.context.ApplicationContextInitializer=\
16 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
17 org.springframework.boot.context.ContextIdApplicationContextInitializer,\
18 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
19 org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
20 org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
21 
22 # Application Listeners
23 org.springframework.context.ApplicationListener=\
24 org.springframework.boot.ClearCachesApplicationListener,\
25 org.springframework.boot.builder.ParentContextCloserApplicationListener,\
26 org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
27 org.springframework.boot.context.FileEncodingApplicationListener,\
28 org.springframework.boot.context.config.AnsiOutputApplicationListener,\
29 org.springframework.boot.context.config.ConfigFileApplicationListener,\
30 org.springframework.boot.context.config.DelegatingApplicationListener,\
31 org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
32 org.springframework.boot.context.logging.LoggingApplicationListener,\
33 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
34 
35 # Environment Post Processors
36 org.springframework.boot.env.EnvironmentPostProcessor=\
37 org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
38 org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
39 org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
40 org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
41 
42 # Failure Analyzers
43 org.springframework.boot.diagnostics.FailureAnalyzer=\
44 org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
45 org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
46 org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
47 org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
48 org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
49 org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
50 org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
51 org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
52 org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
53 org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
54 org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
55 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
56 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
57 
58 # FailureAnalysisReporters
59 org.springframework.boot.diagnostics.FailureAnalysisReporter=\
60 org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
View Code
   spring-boot-autoconfigure-2.2.2.RELEASE.jar - 该配置文件内容如下:
  1 # Initializers
  2 org.springframework.context.ApplicationContextInitializer=\
  3 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
  4 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
  5 
  6 # Application Listeners
  7 org.springframework.context.ApplicationListener=\
  8 org.springframework.boot.autoconfigure.BackgroundPreinitializer
  9 
 10 # Auto Configuration Import Listeners
 11 org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
 12 org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
 13 
 14 # Auto Configuration Import Filters
 15 org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
 16 org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
 17 org.springframework.boot.autoconfigure.condition.OnClassCondition,\
 18 org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
 19 
 20 # Auto Configure
 21 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 22 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
 23 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
 24 org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
 25 org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
 26 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
 27 org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
 28 org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
 29 org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
 30 org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
 31 org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
 32 org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
 33 org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
 34 org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
 35 org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
 36 org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
 37 org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
 38 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
 39 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
 40 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
 41 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
 42 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
 43 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
 44 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
 45 org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
 46 org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
 47 org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
 48 org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
 49 org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
 50 org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
 51 org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
 52 org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
 53 org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
 54 org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
 55 org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
 56 org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
 57 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
 58 org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
 59 org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
 60 org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
 61 org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
 62 org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
 63 org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
 64 org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
 65 org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
 66 org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
 67 org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
 68 org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
 69 org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
 70 org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
 71 org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
 72 org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
 73 org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
 74 org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
 75 org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
 76 org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
 77 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
 78 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
 79 org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
 80 org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
 81 org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
 82 org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
 83 org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
 84 org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
 85 org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
 86 org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
 87 org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
 88 org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
 89 org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
 90 org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
 91 org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
 92 org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
 93 org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
 94 org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
 95 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
 96 org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
 97 org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
 98 org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
 99 org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
100 org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
101 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
102 org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
103 org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
104 org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
105 org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
106 org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
107 org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
108 org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
109 org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
110 org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
111 org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
112 org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
113 org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
114 org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
115 org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
116 org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
117 org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
118 org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
119 org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
120 org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
121 org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
122 org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
123 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
124 org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
125 org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
126 org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
127 org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
128 org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
129 org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
130 org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
131 org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
132 org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
133 org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
134 org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
135 org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
136 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
137 org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
138 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
139 org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
140 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
141 org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
142 org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
143 org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
144 org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
145 org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
146 
147 # Failure analyzers
148 org.springframework.boot.diagnostics.FailureAnalyzer=\
149 org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
150 org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
151 org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
152 org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
153 org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
154 
155 # Template availability providers
156 org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
157 org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
158 org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
159 org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
160 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
161 org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
View Code

   2) loadSpringFactories 方法

   SpringFactoriesLoader#loadSpringFactories

 1 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
 2     //到缓存中读取
 3     MultiValueMap<String, String> result = cache.get(classLoader);
 4     //如果存在则直接返回
 5     if (result != null) {
 6         return result;
 7     }
 8 
 9     try {
10         //获取所以jar包META-INF/spring.factories对应的URL
11         Enumeration<URL> urls = (classLoader != null ?
12                                  classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
13                                  ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
14         result = new LinkedMultiValueMap<>();
15         //遍历数据
16         while (urls.hasMoreElements()) {
17             URL url = urls.nextElement();
18             //获取到资源数据
19             UrlResource resource = new UrlResource(url);
20             //加载配置文件
21             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
22             //遍历解析配置文件
23             for (Map.Entry<?, ?> entry : properties.entrySet()) {
24                 String factoryTypeName = ((String) entry.getKey()).trim();
25                 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
26                     //将spring.factories配置文件数据放进结果集
27                     result.add(factoryTypeName, factoryImplementationName.trim());
28                 }
29             }
30         }
31         //加入缓存
32         cache.put(classLoader, result);
33         //返回结果
34         return result;
35     }
36     catch (IOException ex) {
37         throw new IllegalArgumentException("Unable to load factories from location [" +
38                                            FACTORIES_RESOURCE_LOCATION + "]", ex);
39     }
40 }

   读取完spring.factories后,把读取到的内容(13个key)存储到枚举类中,然后遍历枚举类,将里边内容都add到一个map(result)里边去,最后把classloader以及遍历的结果都放入cache中,提高加载资源的效率。

   3. createSpringFactoriesInstances

  目前已经取出所有的配置,但还没有进行初始化,该方法是实例化对象

  SpringApplication#createSpringFactoriesInstances

 1 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
 2                                                    ClassLoader classLoader, Object[] args, Set<String> names) {
 3     List<T> instances = new ArrayList<>(names.size());
 4     for (String name : names) {
 5         try {
 6             Class<?> instanceClass = ClassUtils.forName(name, classLoader);
 7             Assert.isAssignable(type, instanceClass);
 8             //获取构造方法
 9             Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
10             //实例化对象
11             T instance = (T) BeanUtils.instantiateClass(constructor, args);
12             //加入instances实例列表
13             instances.add(instance);
14         } catch (Throwable ex) {
15             throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
16         }
17     }
18     return instances;
19 }

   四、 SPI机制的应用

   Java 标准库:用于动态加载 JDBC 驱动程序、JAXP 解析器等。

   Spring Boot:使用 META-INF/spring.factories 文件进行自动配置和插件管理。

   第三方库:许多开源库和框架使用 SPI 来提供可插拔的功能和扩展点。

   五、总结

   SPI 机制具备以下特点:

   1. 灵活性

   服务实现可以在运行时动态加载和替换,无需修改应用程序代码。

   2. 模块化

   提供者和消费者的耦合度低,可以在不同的模块中实现服务,支持插件式架构。

   3. 易于扩展

   通过添加新的服务提供者,可以轻松扩展系统的功能,而无需修改已有的代码。

   总的来说,SPI 机制提供了一种灵活的方式来实现服务的动态发现和加载,支持系统的高内聚和低耦合设计。它在 Java 标准库和许多框架(如 Spring Boot)中得到了广泛应用,能够有效地支持插件式架构和模块化设计。

 

  参考链接:

  https://www.baiyp.ren/SpringBoot%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-SPI%E6%9C%BA%E5%88%B6.html

posted @ 2024-08-07 10:58  欢乐豆123  阅读(295)  评论(0编辑  收藏  举报