Spring Boot的扩展机制(类SPI)

我们已经知道Java的SPI机制是一个良好的协同第三方扩展的方案,Spring Boot也借鉴了类似的手段。关于Java SPI机制原理可以点击这里《Java SPI机制》。当然这种机制并不是Spring Boot才有,Spring早期设计就已经考虑了这种功能

一、Spring的扩展机制

Spring约定,在jar包(classpath)下新建META-INF\spring.factories文件,文件内容为Properties格式,其中key可以是接口、注解、或者抽象类的全类名。value为相应的实现全类名,当存在多个实现类时,用“,”进行分割。Spring在启动时,会去读取加载这些类。其中负责读取的类是:
org.springframework.core.io.support.SpringFactoriesLoader
注意,这个类于Spring 3.2就有了,并不是Spring Boot时期出现的,但是在Spring Boot出现之前,几乎没有第三方组件使用这个机制,只有Spring内部会使用,直到Spring Boot出现,为了实现第三方组件自动装配,这个类才被人所熟知,足以证明Spring设计的前瞻性,官方解释是:

框架内部使用的通用工厂加载机制。
SpringFactoriesLoader从“META-INF/spring.factories”文件中加载并实例化给定类型的工厂,这些文件可能存在于类路径中的多个JAR文件中。spring.factories文件必须采用Properties格式,其中键是接口或抽象类的完全限定名,值是以逗号分隔的实现类名列表。例如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
其中example.MyService是接口的名称,MyServiceImpl1和MyServiceImpl2是两个实现。
自:3.2
作者:
Arjen Poutsma、Juergen Hoeller、Sam Brannen

Spring把这个命名为工厂,这是出于什么想法?

二、Spring内部使用

上面说到,Spring Boot出现之前,这个机制一直是Spring内部使用,我们来看看Spring内部是如何使用的:
org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

这里我们只关注第6行:
org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)
这里是调用了SpringFactoriesLoader去找META-INF\spring.factories里配置的文件,并且限定了类型为:
org.springframework.context.ApplicationContextInitializer

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

这是个接口类型,我们可以了解下他的作用是什么:
官方的解释是:

用于在刷新之前初始化Spring ConfigurableApplicationContext的回调接口。
通常在需要对应用程序上下文进行编程初始化的web应用程序中使用。例如,针对上下文环境注册属性源或激活配置文件。请参阅ContextLoader和FrameworkServlet支持,分别声明“contextInitializerClasses”上下文参数和init参数。
建议ApplicationContextInitializer处理器检测Spring的有序接口是否已实现,或者@Order注释是否存在,如果存在,则在调用之前对实例进行相应排序。
自:3.1
另请参见:
org.springframework.web.context.ContextLoader.customizeContext, org.springframework.web.context.ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, org.springframework.web.servlet.FrameworkServlet.setContextInitializerClasses, org.springframework.web.servlet.FrameworkServlet.applyInitializers
作者:克里斯梁
类型参数: –应用程序上下文类型
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}

三、Spring Boot自动装配使用

我们直接检索谁调用了SpringFactoriesLoader的方法,发现org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames()的调用地方有三处(除去内部调用的地方):

org.springframework.core.io.support.SpringFactoriesLoader#loadFactories()的调用地方有2处(除去内部调用的地方):

都是自动装配调用的,到这我们至少可以确定Spring提供的扩展机制目前只被2中情况使用,一种是Spring内部启动初始化,一种是自动装配扩展,至于Spring Boot的自动装配请参考这篇文章:
Spring Boot自动装配原理

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值