重点提示:在我博客中的所有的源码分析的实例,我都将会放到github上,感兴趣的朋友可以下载下来调试运行,我相信还是可以有所收获的。我的目的是让所有读到我博客的朋友都可以了解到有价值的东西,学习到java核心的原理,使用起来更加得心应手。
所有实例的GitHub地址:https://github.com/mh47838704/JavaExample ,我会不定期的更新代码,所有大家可以长期关注一下。
前一段时间公司内部领导希望我们目前的产品的后台开发使用微服务,让我们调研分析一下,与此同时有朋友公司在使用dubbo,所以打算看看dubbo。
首先去dubbo的官网看一下官网的文档,文档描述的很详细,对dubbo的整体的架构、运维部署、扩展机制等都做了详细的介绍。并提出如何使用Java开发出稳定产品的一些心得体会,这些心得体会让我深有同感,所以推荐大家也去看看,另外还强烈推荐把《Effective Java》反复研读。
在了解了dubbo的框架和设计顺便下载了dubbo的源码看了之后呢,里面有一个spi机制在之前没有接触过,通过查看java的官方文档了解到这种机制也是java提供的一种可扩展的一种编程机制,通过这种机制类似于插件机制,非常方便程序的扩展。
SPI机制分析
如果英文好而且又对Java理解的很深的,可以直接查看oracle的官网的介绍https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html ,在该篇文章中对什么是spi、spi的优点、如何开发自己的spi都进行详细的介绍。本篇文章将按照官方的文档,设计并测试相应的spi实例,并通过接口和spi对比测试来区分两者之间的区别。
首先我们定义一个HelloService的接口,并编写两个实现类,类的关系如下图所示
HelloService.java
package com.mh.JavaExample.spi.imp;
/**
* SPI机制分析实例
* Start at: 2018/5/1 16:14
*
* @author muhong
*/
public interface HelloService {
void sayHello();
}
ChinaHelloService.java
package com.mh.JavaExample.spi.imp;
/**
* 中国式的招呼
* Start at: 2018/5/1 16:17
*
* @author muhong
*/
public class ChinaHelloService implements HelloService {
@Override
public void sayHello() {
System.out.println("你好,世界!");
}
}
AmericaHelloService.java
package com.mh.JavaExample.spi.imp;
/**
* 美国式的招呼
* Start at: 2018/5/1 16:18
*
* @author muhong
*/
public class AmericaHelloService implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello world!");
}
}
接口测试:
在我们平常编程的时候,我们使用统一接口自定义实现的方式进行扩展,下面是基于接口的实现方式:
package com.mh.JavaExample.spi;
import com.mh.JavaExample.spi.imp.AmericaHelloService;
import com.mh.JavaExample.spi.imp.ChinaHelloService;
import com.mh.JavaExample.spi.imp.HelloService;
import org.junit.Test;
/**
* 测试接口
* Start at: 2018/5/1 16:22
*
* @author muhong
*/
public class TestMain {
@Test
public void testInterface(){
HelloService ch = new ChinaHelloService();
ch.sayHello();
HelloService en = new AmericaHelloService();
en.sayHello();
}
}
基于SPI的扩展的测试
package com.mh.JavaExample.spi;
import org.junit.Test;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* 测试SPI
* 关于SPI的jar包的打包方式,可以参考oracle的官网说明:https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html
* 本次SPI的测试jar包也是参考该说明打包的:
* 1、和spi相关的class文件拷贝到一个临时目录下/tmp(用户自定创建,和打包无关)
* 2、在该目录下创建META-INF目录,在META-INF目录下创建services目录,在目录下创建com.mh.JavaExample.spi.HelloService文件
* 3、在该文件中输入SPI接口的名字,如com.mh.JavaExample.spi.AmericaHelloService
* 4、进入到临时根目录/tmp中,执行命令jar cvf AmericaHelloService.jar -C . .
* 5、在该根目录下就可以看到AmericaHelloService.jar了
* Start at: 2018/5/1 17:25
*
* @author muhong
*/
public class TestMain2 {
@Test
public void testSpi(){
ServiceLoader services = ServiceLoader.load(HelloService.class);
for (Iterator iterator = services.iterator(); iterator.hasNext(); ) {
HelloService helloService = iterator.next();
helloService.sayHello();
}
}
}
源码分析
相关的源码我已经放到github上了,下面是和该实例相关的类和jar包,通过分析运行这些类和jar包可以对该机制有深入的了解,下面是代码结构图:
在上面的圈中的文件中:
test目录下的spi.imp中的代码文件是用于接口测试的。
在lib中的三个jar包是用于spi测试的,其中HelloService.jar是属于接口包,ChinaHelloService.jar和AmericaHelloService.jar是SPI的实现类,编写打包的格式如下:
总结
通过阅读oracle官方的介绍,可以完成自定义spi的开发,如果英文不是特别好的朋友,可以先查阅相关的博客资料,然后结合我的代码去调试即可有深入的了解。