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机制:来自《Apache Dubbo与实战》一书中截图。
@SPI注解可以使用在类,接口和枚举上。
主要作用就是标记这个接口是一个 Dubbo SPI接口,即一个扩展点。 value,表示可以设置默认的实现类
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface SPI { /** * default extension name */ 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 //@Activate public class MyAdaptiveLog implements MyLog{ @Override public void debug() { System.out.println("==========dubbo MyAdaptiveLog"); } }2、创建配置文件
在项目resources目录下新建一个META-INF/dubbo/文件夹。
- 在 META-INF/dubbo/目录下,创建一个文件,文件名为该SPI接口的全限定名。
- 文件内容是key=具体实现类的全限定名,如果有多个,则用分行符分隔。
在 代码中通过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(); // 获取实现了@Adaptive注解的实现类 System.out.println("MyLog 自适应扩展点:" + extensionLoader.getAdaptiveExtension()); System.out.println("MyLog 默认扩展点协议:" + extensionLoader.getDefaultExtension()); System.out.println("MyLog 获取所有扩展点:" + extensionLoader.getSupportedExtensions()); }
– 求知若饥,虚心若愚。