SpringBoot的SPI機(jī)制源碼解析
一、從java類加載機(jī)制說起
java中的類加載器負(fù)載加載來自文件系統(tǒng)、網(wǎng)絡(luò)或者其他來源的類文件。jvm的類加載器默認(rèn)使用的是雙親委派模式。
三種默認(rèn)的類加載器Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(Application ClassLoader)每一個(gè)類加載器都確定了從哪些位置加載文件。于此同時(shí)我們也可以通過繼承java.lang.classloader實(shí)現(xiàn)自己的類加載器。
- Bootstrap ClassLoader:負(fù)責(zé)加載JDK自帶的rt.jar包中的類文件,是所有類加載的父類
- Extension ClassLoader:負(fù)責(zé)加載java的擴(kuò)展類庫從jre/lib/ect目錄或者java.ext.dirs系統(tǒng)屬性指定的目錄下加載類,是System ClassLoader的父類加載器
- System ClassLoader:負(fù)責(zé)從classpath環(huán)境變量中加載類文件
1.1 雙親委派模型
當(dāng)一個(gè)類加載器收到類加載任務(wù)時(shí),會先交給自己的父加載器去完成,因此最終加載任務(wù)都會傳遞到最頂層的BootstrapClassLoader,只有當(dāng)父加載器無法完成加載任務(wù)時(shí),才會嘗試自己來加載。
具體:根據(jù)雙親委派模式,在加載類文件的時(shí)候,子類加載器首先將加載請求委托給它的父加載器,父加載器會檢測自己是否已經(jīng)加載過類,如果已經(jīng)加載則加載過程結(jié)束,如果沒有加載的話則請求繼續(xù)向上傳遞,直至Bootstrap ClassLoader。如果請求向上委托過程中,如果始終沒有檢測到該類已經(jīng)加載,則Bootstrap ClassLoader開始嘗試從其對應(yīng)路勁中加載該類文件,如果失敗則由子類加載器繼續(xù)嘗試加載,直至發(fā)起加載請求的子加載器為止。
采用雙親委派模式可以保證類型加載的安全性,不管是哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的BootstrapClassLoader來加載的,只有父類無法加載時(shí),自己才嘗試加載,這樣就可以保證任何的類加載器最終得到的都是同樣一個(gè)Object對象
protected Class<?> loadClass(String name, boolean resolve) { synchronized (getClassLoadingLock(name)) { // 首先,檢查該類是否已經(jīng)被加載,如果從JVM緩存中找到該類,則直接返回 Class<?> c = findLoadedClass(name); if (c == null) { try { // 遵循雙親委派的模型,首先會通過遞歸從父加載器開始找, // 直到父類加載器是BootstrapClassLoader為止 if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) {} if (c == null) { // 如果還找不到,嘗試通過findClass方法去尋找 // findClass是留給開發(fā)者自己實(shí)現(xiàn)的,也就是說 // 自定義類加載器時(shí),重寫此方法即可 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } }
1.2 雙親委派模型缺陷
在雙親委派模型中,子類加載器可以使用父類加載器已經(jīng)加載的類,而父類加載器無法使用子類加載器已經(jīng)加載的。這就導(dǎo)致了雙親委派模型并不能解決所有的類加載器問題。
**案例:**Java 提供了很多服務(wù)提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實(shí)現(xiàn)。常見的 SPI 有 JDBC、JNDI、JAXP 等,這些SPI的接口由核心類庫提供,卻由第三方實(shí)現(xiàn),這樣就存在一個(gè)問題:SPI 的接口是 Java 核心庫的一部分,是由BootstrapClassLoader加載的;SPI實(shí)現(xiàn)的Java類一般是由AppClassLoader來加載的。BootstrapClassLoader是無法找到 SPI 的實(shí)現(xiàn)類的,因?yàn)樗患虞dJava的核心庫。它也不能代理給AppClassLoader,因?yàn)樗亲铐攲拥念惣虞d器。也就是說,雙親委派模型并不能解決這個(gè)問題。
1.3 使用線程上下文類加載器(ContextClassLoader)加載
如果不做任何的設(shè)置,Java應(yīng)用的線程的上下文類加載器默認(rèn)就是AppClassLoader。在核心類庫使用SPI接口時(shí),傳遞的類加載器使用線程上下文類加載器,就可以成功的加載到SPI實(shí)現(xiàn)的類。線程上下文類加載器在很多SPI的實(shí)現(xiàn)中都會用到。
通常我們可以通過Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()獲取線程上下文類加載器。
1.4 使用類加載器加載資源文件,比如jar包
類加載器除了加載class外,還有一個(gè)非常重要功能,就是加載資源,它可以從jar包中讀取任何資源文件,比如,ClassLoader.getResources(String name)方法就是用于讀取jar包中的資源文件。
//獲取資源的方法 public Enumeration<URL> getResources(String name) throws IOException { Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2]; if (parent != null) { tmp[0] = parent.getResources(name); } else { tmp[0] = getBootstrapResources(name); } tmp[1] = findResources(name); return new CompoundEnumeration<>(tmp); }
它的邏輯其實(shí)跟類加載的邏輯是一樣的,首先判斷父類加載器是否為空,不為空則委托父類加載器執(zhí)行資源查找任務(wù),直到BootstrapClassLoader,最后才輪到自己查找。而不同的類加載器負(fù)責(zé)掃描不同路徑下的jar包,就如同加載class一樣,最后會掃描所有的jar包,找到符合條件的資源文件。
// 使用線程上下文類加載器加載資源 public static void main(String[] args) throws Exception{ // Array.class的完整路徑 String name = "java/sql/Array.class"; Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name); while (urls.hasMoreElements()) { URL url = urls.nextElement(); System.out.println(url.toString()); } }
二、Spring中SPI機(jī)制實(shí)現(xiàn)
2.1 SPI機(jī)制
2.1.1 SPI思想
SPI的全名為Service Provider Interface.這個(gè)是針對廠商或者插件的。
SPI的思想:系統(tǒng)里抽象的各個(gè)模塊,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設(shè)計(jì)里,我們一般推薦模塊之間基于接口編程,模塊之間不對實(shí)現(xiàn)類進(jìn)行硬編碼。一旦代碼里涉及具體的實(shí)現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實(shí)現(xiàn),就需要修改代碼。為了實(shí)現(xiàn)在模塊裝配的時(shí)候能不在程序里動態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。
java spi就是提供這樣的一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。
2.1.2 SPI約定
當(dāng)服務(wù)的提供者,提供了服務(wù)接口的一種實(shí)現(xiàn)之后,在jar包的META-INF/services/目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過該jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類名,并裝載實(shí)例化,完成模塊的注入。通過這個(gè)約定,就不需要把服務(wù)放在代碼中了,通過模塊被裝配的時(shí)候就可以發(fā)現(xiàn)服務(wù)類了。
2.2 SPI使用案例
common-logging apache最早提供的日志的門面接口。只有接口,沒有實(shí)現(xiàn)。具體方案由各提供商實(shí)現(xiàn), 發(fā)現(xiàn)日志提供商是通過掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過讀取該文件的內(nèi)容找到日志提工商實(shí)現(xiàn)類。只要我們的日志實(shí)現(xiàn)里包含了這個(gè)文件,并在文件里制定 LogFactory工廠接口的實(shí)現(xiàn)類即可。
2.3 Springboot中的類SPI擴(kuò)展機(jī)制
在springboot的自動裝配過程中,最終會加載META-INF/spring.factories文件,而加載的過程是由SpringFactoriesLoader加載的。從CLASSPATH下的每個(gè)Jar包中搜尋所有META-INF/spring.factories配置文件,然后將解析properties文件,找到指定名稱的配置后返回。需要注意的是,其實(shí)這里不僅僅是會去ClassPath路徑下查找,會掃描所有路徑下的Jar包,只不過這個(gè)文件只會在Classpath下的jar包中。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; // spring.factories文件的格式為:key=value1,value2,value3 // 從所有的jar包中找到META-INF/spring.factories文件 // 然后從文件中解析出key=factoryClass類名稱的所有value值 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); // 取得資源文件的URL Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); // 遍歷所有的URL while (urls.hasMoreElements()) { URL url = urls.nextElement(); // 根據(jù)資源文件URL解析properties文件,得到對應(yīng)的一組@Configuration類 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); // 組裝數(shù)據(jù),并返回 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; }
三、源碼分析
先找到一個(gè)入口類getSpringFactoriesInstances
3.1 getSpringFactoriesInstances
根據(jù)類型獲取META-INF/spring.factories文件中對應(yīng)的實(shí)現(xiàn)類
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[]{}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //從META-INF/spring.factories中加載對應(yīng)類型的類的自動配置類 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 加載上來后反射實(shí)例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 對實(shí)例列表進(jìn)行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
3.2 SpringFactoriesLoader
我們看看它的源碼(精簡):
public abstract class SpringFactoriesLoader { private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) { } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { } }
這個(gè)類我們有利于我們繼續(xù)往下找線索的代碼是這一行:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
有了這一行代碼,我們大致就可以猜出來這個(gè)SpringFactoriesLoader類大概是干嘛的了吧?它的兩個(gè)核心方法一個(gè)是用來尋找spring.factories文件中的Factory名稱的,一個(gè)是用來尋找類的。
3.3 loadFactoryNames
在該方法里,首先拿到ClassLoader,然后加載FactoryNames,加載類型(type)為ApplicationContextInitializer,類加載器(classLoader)為剛剛拿到的類加載器,返回值放入一個(gè)Set中,為的是確保沒有重復(fù)的FactoryName,這是因?yàn)樵谥蠹虞d的兩個(gè)spring.factories配置文件中有兩個(gè)重復(fù)的FactoryName。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
這一步是為了使用給定的ClassLoader去給定的FACTORIES_RESOURCE_LOCATION中加載全部的工廠類
可以看到,加載的配置文件在META-INF下,名稱為spring.factories,該配置文件一共有兩個(gè),且配置文件中,每個(gè)段落第一行為Key,后邊為value,讀取時(shí)會通過key將所有的value拿出來
在配置文件中我們發(fā)現(xiàn),key和value都是包名加類名的字符串,因此Springboot在讀取文件后,是通過反射生成的類
3.3.1 spring-boot-2.2.2.RELEASE.jar
該配置文件內(nèi)容如下:
# 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.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ 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,\ org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\ 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.NoSuchMethodFailureAnalyzer,\ 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
3.3.2 spring-boot-autoconfigure-2.2.2.RELEASE.jar
該配置文件內(nèi)容如下:
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\ org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\ org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
3.4 loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //到緩存中讀取 MultiValueMap<String, String> result = cache.get(classLoader); //如果存在則直接返回 if (result != null) { return result; } try { //獲取所以jar包META-INF/spring.factories對應(yīng)的URL Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); //遍歷數(shù)據(jù) while (urls.hasMoreElements()) { URL url = urls.nextElement(); //獲取到資源數(shù)據(jù) UrlResource resource = new UrlResource(url); //加載配置文件 Properties properties = PropertiesLoaderUtils.loadProperties(resource); //遍歷解析配置文件 for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { //將spring.factories配置文件數(shù)據(jù)放進(jìn)結(jié)果集 result.add(factoryTypeName, factoryImplementationName.trim()); } } } //加入緩存 cache.put(classLoader, result); //返回結(jié)果 return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
讀取完spring.factories后,把讀取到的內(nèi)容(13個(gè)key)存儲到枚舉類中,然后遍歷枚舉類,將里邊內(nèi)容都add到一個(gè)map(result)里邊去,最后把classloader以及遍歷的結(jié)果都放入cache中,提高加載資源的效率。
3.5 createSpringFactoriesInstances
目前已經(jīng)取出所有的配置,但還沒有進(jìn)行初始化,該方法是實(shí)例化對象的
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); //獲取構(gòu)造方法 Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); //實(shí)例化對象 T instance = (T) BeanUtils.instantiateClass(constructor, args); //加入instances實(shí)例列表 instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
以上就是SpringBoot的SPI機(jī)制源碼解析的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot SPI機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA如何設(shè)置SVN提交忽略文件 target.iml
使用IDEA的SVN插件時(shí),可能會遇到提交不必要文件的問題,解決這個(gè)問題有兩種方法:第一種是在IDEA設(shè)置中的File Types下的Ignore files and folders添加需要忽略的文件或文件夾;第二種是使用SVN客戶端TortoiseSVN,在項(xiàng)目目錄點(diǎn)擊右鍵選擇properties2024-10-10SpringBoot、Java 使用 Jsoup 解析 HTML 頁面
這篇文章主要介紹了SpringBoot、Java 使用 Jsoup 解析 HTML 頁面的詳細(xì)步驟,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08Springboot Redis?哨兵模式的實(shí)現(xiàn)示例
本文主要介紹了Springboot Redis?哨兵模式的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01SpringBoot中Shiro緩存使用Redis、Ehcache的方法
這篇文章主要介紹了SpringBoot中Shiro緩存使用Redis、Ehcache的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Springboot結(jié)合Flowable實(shí)現(xiàn)工作流開發(fā)
本文主要介紹了Springboot結(jié)合Flowable實(shí)現(xiàn)工作流開發(fā),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Mybatis-plus動態(tài)條件查詢QueryWrapper的使用案例
mybatis-plus框架功能很強(qiáng)大,把很多功能都集成了,下面這篇文章主要給大家介紹了關(guān)于Mybatis-plus動態(tài)條件查詢QueryWrapper的使用教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07