Java SPI机制博客传送门:https://blog.csdn.net/qq_42402854/article/details/125705159 注意: JDK SPI会一次性实例化扩展点的所有实现。
Dubbo没有使用JDK的SPI机制,而是自己实现的一套SPI机制。在Dubbo的源码中,很多地方会存在下面这样的三种代码:
//获取自适应扩展点
ExtensionLoader.getExtensionLoader(xxx.class).getAdaptiveExtension();
//获取指定名称的扩展点
ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name);
//激活扩展点
ExtensionLoader.getExtensionLoader(xxx.class).getActivateExtension(url, key);
在Dubbo中,SPI贯穿整个Dubbo的核心,所以理解Dubbo中的SPI对于理解Dubbo的原理有着至关重要的作用。
- 在Java中,通过 java.util.ServiceLoader 来发现动态加载具体的实现类到JVM中。
- 在Spring中,SpringFactoriesLoader这个类,它也是一种SPI机制。
- 在Dubbo中,ExtensionLoader这个类,它也是一种SPI机制。
一、Dubbo SPI机制使用
1、Dubbo的 SPI机制
Dubbo的 SPI机制:来自《Apache Dubbo与实战》一书中截图。
1.1 扩展点注解
1.1.1 @SPI注解
@SPI注解可以使用在类,接口和枚举上。
主要作用就是标记这个接口是一个 Dubbo SPI接口,即一个扩展点。 value,表示可以设置默认的实现类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
String value() default "";
}
1.1.2 @Adaptive注解
@Adaptive注解 可以标记在类、接口、枚举类和方法上。
自适应扩展点的知识,表示在运行时使用那个实现类。 当该注解使用在类上时,只能有一个实现类上可以加 @Adaptive注解,如果多个实现类都有该注解会抛出异常。
包装类等其他的自行了解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
1.1.3 @Acticate注解
@Acticate注解 可以标记在类、接口、枚举类和方法上。
默认自动激活,主要使用在多个扩展点实现、还可以根据不同条件被激活的场景中。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
String[] group() default {};
String[] value() default {};
String[] before() default {};
String[] after() default {};
int order() default 0;
}
2、获取Protocol扩展点
public static void main(String[] args) {
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol dubbo = extensionLoader.getExtension("dubbo");
System.out.println("dubbo 指定名称的扩展点:" + dubbo);
System.out.println("dubbo 自适应扩展点:" + extensionLoader.getAdaptiveExtension());
System.out.println("dubbo 默认扩展点协议:" + extensionLoader.getDefaultExtension());
System.out.println("dubbo 获取所有扩展点协议:" + extensionLoader.getSupportedExtensions());
}
Protocol接口源码如下:
二、自定义扩展点
这里写个 demo 感受一下 dubbo的SPI机制。
1、定义扩展点和实现类
1.1 扩展点
@SPI注解:value,表示设置默认的实现类
@SPI("MyLog")
public interface MyLog {
void debug();
}
1.2 实现类
这里定义三个实现类。
@Activate
public class MyLog4j implements MyLog{
@Override
public void debug() {
System.out.println("==========dubbo MyLog4j");
}
}
@Activate
public class MyLogback implements MyLog{
@Override
public void debug() {
System.out.println("==========dubbo MyLogback");
}
}
@Adaptive
public class MyAdaptiveLog implements MyLog{
@Override
public void debug() {
System.out.println("==========dubbo MyAdaptiveLog");
}
}
2、创建配置文件
在项目resources目录下新建一个 META-INF/dubbo/ 文件夹。
- 在 META-INF/dubbo/目录下,创建一个文件,
文件名为该SPI接口的全限定名 。 - 文件内容是
key=具体实现类的全限定名 ,如果有多个,则用分行符分隔。
3、测试,加载实现类
在 代码中通过 org.apache.dubbo.common.extension.ExtensionLoader 来加载具体的实现类。
@Test
public void test() {
ExtensionLoader<MyLog> extensionLoader = ExtensionLoader.getExtensionLoader(MyLog.class);
MyLog myLogback = extensionLoader.getExtension("myLogback");
System.out.println("MyLog 指定名称的扩展点:" + myLogback);
myLogback.debug();
System.out.println("MyLog 自适应扩展点:" + extensionLoader.getAdaptiveExtension());
System.out.println("MyLog 默认扩展点协议:" + extensionLoader.getDefaultExtension());
System.out.println("MyLog 获取所有扩展点:" + extensionLoader.getSupportedExtensions());
}
– 求知若饥,虚心若愚。
|