关于java:源码透视SpringBoot的SPI机制

10次阅读

共计 24666 个字符,预计需要花费 62 分钟才能阅读完成。

一、从 java 类加载机制说起

java 中的类加载器负载加载来自文件系统、网络或者其余起源的类文件。jvm 的类加载器默认应用的是双亲委派模式。三种默认的类加载器 Bootstrap ClassLoader、Extension ClassLoader 和 System ClassLoader(Application ClassLoader)每一个类加载器都确定了从哪些地位加载文件。于此同时咱们也能够通过继承 java.lang.classloader 实现本人的类加载器。

  • Bootstrap ClassLoader:负责加载 JDK 自带的 rt.jar 包中的类文件,是所有类加载的父类
  • Extension ClassLoader:负责加载 java 的扩大类库从 jre/lib/ect 目录或者 java.ext.dirs 零碎属性指定的目录下加载类,是 System ClassLoader 的父类加载器
  • System ClassLoader:负责从 classpath 环境变量中加载类文件

1.1 双亲委派模型

当一个类加载器收到类加载工作时,会先交给本人的父加载器去实现,因而最终加载工作都会传递到最顶层的 BootstrapClassLoader,只有当父加载器无奈实现加载工作时,才会尝试本人来加载。

具体:依据双亲委派模式,在加载类文件的时候,子类加载器首先将加载申请委托给它的父加载器,父加载器会检测本人是否曾经加载过类,如果曾经加载则加载过程完结,如果没有加载的话则申请持续向上传递,直至 Bootstrap ClassLoader。如果申请向上委托过程中,如果始终没有检测到该类曾经加载,则 Bootstrap ClassLoader 开始尝试从其对应路劲中加载该类文件,如果失败则由子类加载器持续尝试加载,直至发动加载申请的子加载器为止。

采纳双亲委派模式能够保障类型加载的安全性,不论是哪个加载器加载这个类,最终都是委托给顶层的 BootstrapClassLoader 来加载的,只有父类无奈加载时,本人才尝试加载,这样就能够保障任何的类加载器最终失去的都是同样一个 Object 对象

protected Class<?> loadClass(String name, boolean resolve) {synchronized (getClassLoadingLock(name)) {
    // 首先,查看该类是否曾经被加载,如果从 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 是留给开发者本人实现的,也就是说
            // 自定义类加载器时,重写此办法即可
           c = findClass(name);
        }
    }
    if (resolve) {resolveClass(c);
    }
    return c;
    }
}

1.2 双亲委派模型缺点

在双亲委派模型中,子类加载器能够应用父类加载器曾经加载的类,而父类加载器无奈应用子类加载器曾经加载的。这就导致了双亲委派模型并不能解决所有的类加载器问题。

案例:Java 提供了很多服务提供者接口(Service Provider Interface,SPI),容许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JNDI、JAXP 等,这些 SPI 的接口由外围类库提供,却由第三方实现,这样就存在一个问题:SPI 的接口是 Java 外围库的一部分,是由 BootstrapClassLoader 加载的;SPI 实现的 Java 类个别是由 AppClassLoader 来加载的。BootstrapClassLoader 是无奈找到 SPI 的实现类的,因为它只加载 Java 的外围库。它也不能代理给 AppClassLoader,因为它是最顶层的类加载器。也就是说,双亲委派模型并不能解决这个问题。

1.3 应用线程上下文类加载器 (ContextClassLoader) 加载

如果不做任何的设置,Java 利用的线程的上下文类加载器默认就是 AppClassLoader。在外围类库应用 SPI 接口时,传递的类加载器应用线程上下文类加载器,就能够胜利的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

通常咱们能够通过 Thread.currentThread().getClassLoader()和 Thread.currentThread().getContextClassLoader()获取线程上下文类加载器。

1.4 应用类加载器加载资源文件,比方 jar 包

类加载器除了加载 class 外,还有一个十分重要性能,就是加载资源,它能够从 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);
}

它的逻辑其实跟类加载的逻辑是一样的,首先判断父类加载器是否为空,不为空则委托父类加载器执行资源查找工作,直到 BootstrapClassLoader,最初才轮到本人查找。而不同的类加载器负责扫描不同门路下的 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 机制实现

2.1 SPI 机制

2.1.1 SPI 思维

SPI 的全名为 Service Provider Interface. 这个是针对厂商或者插件的。

SPI 的思维:零碎里形象的各个模块,往往有很多不同的实现计划,比方日志模块的计划,xml 解析模块、jdbc 模块的计划等。面向的对象的设计里,咱们个别举荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里波及具体的实现类,就违反了可拔插的准则,如果须要替换一种实现,就须要批改代码。为了实现在模块拆卸的时候能不在程序里动静指明,这就须要一种服务发现机制。

java spi 就是提供这样的一个机制:为某个接口寻找服务实现的机制。

2.1.2 SPI 约定

当服务的提供者,提供了服务接口的一种实现之后,在 jar 包的 META-INF/services/ 目录里同时创立一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当内部程序拆卸这个模块的时候,就能通过该 jar 包 META-INF/services/ 里的配置文件找到具体的实现类名,并装载实例化,实现模块的注入。通过这个约定,就不须要把服务放在代码中了,通过模块被拆卸的时候就能够发现服务类了。

2.2 SPI 应用案例

common-logging apache 最早提供的日志的门面接口。只有接口,没有实现。具体计划由各提供商实现,发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory 配置文件,通过读取该文件的内容找到日志提工商实现类。只有咱们的日志实现里蕴含了这个文件,并在文件里制订 LogFactory 工厂接口的实现类即可。

2.3 Springboot 中的类 SPI 扩大机制

在 springboot 的主动拆卸过程中,最终会加载 META-INF/spring.factories 文件,而加载的过程是由 SpringFactoriesLoader 加载的。从 CLASSPATH 下的每个 Jar 包中搜查所有 META-INF/spring.factories 配置文件,而后将解析 properties 文件,找到指定名称的配置后返回。须要留神的是,其实这里不仅仅是会去 ClassPath 门路下查找,会扫描所有门路下的 Jar 包,只不过这个文件只会在 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();
        // 依据资源文件 URL 解析 properties 文件,失去对应的一组 @Configuration 类
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        // 组装数据,并返回
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
}

三、源码剖析

先找到一个入口类 getSpringFactoriesInstances

3.1 getSpringFactoriesInstances

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

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 中加载对应类型的类的主动配置类
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 加载上来后反射实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    // 对实例列表进行排序
    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) {}}

这个类咱们有利于咱们持续往下找线索的代码是这一行:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

有了这一行代码,咱们大抵就能够猜出来这个 SpringFactoriesLoader 类大略是干嘛的了吧?它的两个外围办法一个是用来寻找 spring.factories 文件中的 Factory 名称的,一个是用来寻找类的。

3.3 loadFactoryNames

在该办法里,首先拿到 ClassLoader,而后加载 FactoryNames,加载类型 (type) 为 ApplicationContextInitializer,类加载器 (classLoader) 为刚刚拿到的类加载器,返回值放入一个 Set 中,为的是确保没有反复的 FactoryName,这是因为在之后加载的两个 spring.factories 配置文件中有两个反复的 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,该配置文件一共有两个,且配置文件中,每个段落第一行为 Key,后边为 value,读取时会通过 key 将所有的 value 拿进去

在配置文件中咱们发现,key 和 value 都是包名加类名的字符串,因而 Springboot 在读取文件后,是通过反射生成的类

3.3.1 spring-boot-2.2.2.RELEASE.jar

该配置文件内容如下:

# 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

该配置文件内容如下:

# 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 对应的 URL
        Enumeration<URL> 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 factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    // 将 spring.factories 配置文件数据放进后果集
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        // 退出缓存
        cache.put(classLoader, result);
        // 返回后果
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

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

3.5 createSpringFactoriesInstances

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

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);
            // 获取构造方法
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            // 实例化对象
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            // 退出 instances 实例列表
            instances.add(instance);
        } catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate" + type + ":" + name, ex);
        }
    }
    return instances;
}

如果本文对您有帮忙,欢送 关注 点赞`,您的反对是我保持创作的能源。

转载请注明出处!

正文完
 0