SPI Java和SpringBoot具体实现

1.SPI是什么?

        SPI 全称为服务提供者接口 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。可以轻松实现面向服务的注册与发现,完成服务提供与使用的解耦,并且可以实现动态加载。

上述概念,在各类博客都能搜到。但是如何理解呢?

(1)java中的服务

服务就是提供某种特殊功能的模块,例如某一模块提供数据库增删改查的服务。

直白理解:服务就是为他人提供某种需要。例如:你为公司提供劳动力,医生为你提供医疗服务。

(2)服务注册与发现

服务注册与发现是站在不同的角度表达相同的意思。例如,存在明星A和其粉丝B。对于粉丝B来说,粉丝B向明星A注册了自己。对于明星A来说,明星A发现了粉丝B。

(3)服务提供者与使用者解偶

在java具体实现中,解偶可以通过接口、实现类来体现。

依旧拿明星A和粉丝来举例。明星A提供一个卡帐号(java中接口),粉丝群体1向明星提供应援服务。对于明星A来说,他只需向粉丝群体1提供了一个卡帐号1,向粉丝群体2提供一个帐号2,这样就和多个粉丝群体解偶,不费吹灰之力(低耦合)就得到各个粉丝群体的应援。此外,如果扩展了粉丝群体3,只需要提供帐号3即可(可扩展性)。

(4)动态加载

java中动态加载简单理解就是,在程序需要的时候加载类。

拿明星A和粉丝群体来说,明星如果在需要的时候,可以告知粉丝群体n让其提供应援服务。

SPI其实就是java中利用接口作为桥梁,接口的实现类提供服务。

2. SPI在java和各种框架实现

2.1 SPI JAVA实现

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件,文件内容是实现该服务接口的具体类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。JDK提供服务实现查找的一个工具类:java.util.ServiceLoader。

代码示例:
自定义接口和实现类:

public interface Subscribe {
	void follow();

}
//接口实现1
public class MySubscribe implements Subscribe {
	@Override
	public void follow() {
		System.out.println("测试自己的订阅");
	}
}

//接口实现2
public class OtherSubscribe implements Subscribe{
	@Override
	public void follow() {
		System.out.println("测试别人的订阅");
	}
}

添加接口配置文件并配置实现类:

在resources目录下创建META-INF/services目录,并在目录下创建以接口全限定名为文件名的文件com.service.javaspi.Subscribe。文件内容为接口两个实现类的全限定名:

com.service.javaspi.OtherSubscribe
com.service.javaspi.MySubscribe

加载并启动:

注意:我这里启动方式是在SpringBoot应用程序的自定义监听器,普通的java程序可以直接在main()方法中加载。

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent>{
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		System.out.println("容器初始化结束,可以执行自定义初始化");
		ServiceLoader<Subscribe> services = ServiceLoader.load(Subscribe.class);
		for (Subscribe sub : services) {
			sub.follow();
		}
	}
}

输出:

容器初始化结束,可以执行自定义初始化
测试别人的订阅
测试自己的订阅

2.2 SPI SpringBoot实现

总述:SpringBoot的SPI机制和Java SPI思想类似。SpringBoot在自动装配过程中,会扫描所有路径下的jar包,根据META-INF/spring.factories文件中的类全限定名,由SpringFactoriesLoader加载各种类。

在SpringBoot启动过程第一步,创建SpringApplication对象时,会加载多个初始化器和监听器,加载的方法就是loadFactoryNames。该方法源码如下:

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;
}

可以看到,它并没有采用Java SPI机制来加载这些类,不过原理差不多。都是通过一个配置文件,加载并解析文件内容,然后通过反射创建实例。

2.3 SpringBoot SPI自定义实现:

和Java SPI实现类似,首先在resources目录下创建META-INF/spring.factories文件,在文件中配置自定义初始化器。

org.springframework.context.ApplicationContextInitializer=com.service.javaspi.MyContextInitializer

然后定义一个MyContextInitializer类

@Component
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		// TODO Auto-generated method stub
		System.out.println("测试SpringBoot自定义SPI");
	}

}

测试输出:

测试SpringBoot自定义SPI

注意,这里自定义的初始化器日志打印在SpringBoot启动记录中间部分。

参考链接:

https://www.jianshu.com/p/9682b932b8b9

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值