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  阅读(83)  评论(0编辑  收藏  举报