dubbo原理之dubbo SPI
本节目的:Dubbo SPI是dubbo所有扩展机制的核心,所以非常之重要!!所以为了让大家全面的了解dubbo SPI机制,见见结合dubbo源码以及官网,整理了dubbo spi机制。以及对比Java原生的spi,dubbo spi的其他特性。本章所有案例代码已经上传https://github.com/lengweijian/dubbo.git . lazy的小伙伴儿可以自行下载调试。
1、SPI简介
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。Dubbo良好的扩展性与两个方面是离不开的,一是框架针对不同的应用场景,恰到好处地使用各种设计模式。二就是本章介绍的加载机制,基于Dubbo SPI机制实现了dubbo的整个框架的接口和接口的实现类的良好解偶,奠定了扩展性的基础。
2、Java SPI
在介绍dubbo spi之前,先介绍Java spi。
- 在 META-INF/services/ 目录中创建以接口全限定名命名的文件,该文件内容为API具体实现类的全限定名
- 使用 ServiceLoader 类动态加载 META-INF 中的实现类
- 如 SPI 的实现类为 Jar 则需要放在主程序 ClassPath 中
- API 具体实现类必须有一个不带参数的构造方法
第一步,创建一个服务的接口,用于向外提供服务。
public interface DemoService {
void sayHi(String msg);
}
第二步,再创建服务接口的第一个实现。
public class DemoServiceImpl_01 implements DemoService {
@Override
public void sayHi(String msg) {
System.out.println("hello !!" + msg + "DemoServiceImpl_01....");
return ;
}
}
第三步,再创建服务接口的第二个实现。
public class DemoServiceImpl_02 implements DemoService {
@Override
public void sayHi(String msg) {
System.out.println("hello !!" + msg + "DemoServiceImpl_02....");
return ;
}
}
第四步,在classpath下创建名称为文件夹【/META-INF/services】,这里名称、包文件层级固定,创建一个文件,名称为刚才服务接口的全路径,因为我这里的全路径为【com.atlwj.aop.service.DemoService】,所以你要改成你本地的接口全路径。文件的内容如下:
com.atlwj.aop.service.impl.DemoServiceImpl_01
com.atlwj.aop.service.impl.DemoServiceImpl_02
说明:文件内容为服务提供接口的实现类的全路径,路径和路径之间要换行。
第五步,创建测试类:
public class SPI_APP {
public static void main(String[] args) {
// 这句话的意思就是加载DemoService接口的实现类
ServiceLoader<DemoService> serviceLoader = ServiceLoader.load(DemoService.class);
// 通过迭代器遍历每个服务实现
Iterator<DemoService> iterator = serviceLoader.iterator();
while (iterator.hasNext() && serviceLoader != null){
DemoService helloService = iterator.next();
helloService.sayHi("lengweijian");
}
}
}
运行结果:
应用场景:JDBC
2.Dubbo SPI(重点)
简介:跟Java SPI相比,dubbo spi做了一定的优化和改进。主要包括:
- jdk标准的SPI会一次性的实例化所有扩展点的实现类,如果其中有的实现类初始化很耗时,那么如果这个类不用的话,它也会加载,就会出现资源的浪费。
- 如果扩展加载失败,则连扩展的名称也获取不到了。
- dubbo spi增加了IOC(dubo仅仅支持setter方法的注入)和AOP的支持。下边会介绍。
2.1 dubbo spi 使用
第一步,创建服务提供接口,注意这里的@SPI注解不能少!!!
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface DemoService {
void sayHi(String msg);
}
第二步,创建两个实现类
import com.atlwj.aop.service.DemoService;
public class DemoServiceImpl_01 implements DemoService {
@Override
public void sayHi(String msg) {
System.out.println("hello !!" + msg + "DemoServiceImpl_01.....");
return ;
}
}
import com.atlwj.aop.service.DemoService;
public class DemoServiceImpl_02 implements DemoService {
@Override
public void sayHi(String msg) {
System.out.println("hello !!" + msg + "DemoServiceImpl_02.....");
return ;
}
}
第三步,在classpath下创建文件夹【/META-INF/dubbo】名称位置固定,创建名称为服务提供接口的全路径的文件,因为见见的是【com.atlwj.aop.service.DemoService】
第四步,将服务提供接口实现类的全路径写在刚才第三步的文件里,但是跟Java spi不同的是可以给每个实现类添加key值,用【=】分开,如:
第五步,创建测试类
import com.atlwj.aop.service.DemoService;
import org.apache.dubbo.common.extension.ExtensionLoader;
/**
* dubbo spi特性:
* 1.自动注入: setter
* 2.AOP: Wrapper01、Wrapper02
*/
public class Dubbo_SPI_APP {
public static void main(String[] args) {
// <1> 获取扩展加载器
ExtensionLoader<DemoService> extensionLoader = ExtensionLoader.getExtensionLoader(DemoService.class);
// <2> 根据指定的key值获取你想要的服务实现类实例
DemoService extension = extensionLoader.getExtension("02");
// <3> 调用服务方法
extension.sayHi("lengweijian");
}
}
启动测试类:
2.2 dubbo spi 特性
- 特性一:AOP
第一步,创建增强类,实现服务提供接口。
public class DemoServiceWrapper implements DemoService {
private DemoService demoService;
// 在这里将服务提供接口使用构造器注入进来
public DemoServiceWrapper(DemoService demoService){ //具体的实现类
this.demoService = demoService;
}
@Override
public void sayHi(String msg) {
System.out.println("Wrapper" + "before.....");
// before 方法前置增强
demoService.sayHi(msg);
// after 方法后置增强
System.out.println("Wrapper" + "after.....");
}
}
第二步,在【com.atlwj.aop.service.DemoService】文件里边加上包装类的全路径
启动测试类:
结果发现,包装类实现了对目标类的增强。
如果重复包装,重复以上操作。当前的文件下:
结果:
以上是dubbo spi aop的特性展示。
总结:dubbo spi与java spi不同的是,可以在服务提供接口文件内指定实现类的key值,然后获取扩展类的时候,可以指定的获取,不用迭代或者判断。
并且可以对目标实现的目标方法进行没有代码侵入性的多重增强。