Dubbo基础---Java SPI

       SPI即Service Provider Interface, 服务提供接口;

       

       SPI是在面向接口编程的背景下产生的,  调用方只关心函数入参、返回值, 不关心函数实现;在编码中调用方不用加载实现类, 从而实现了热插拔。


SPI规则:

1、定义接口类;

2、创建/resources/META-INF/接口类  文件;

3、在文件中添加实现类的完整类名;

创建接口:

public interface IService {
    void doSomeThing();
}

定义多种实现:

public class HiService implements IService {
    @Override
    public void doSomeThing() {
        System.out.println("HiService is called");
    }
}
public class HelloService implements IService {
    @Override
    public void doSomeThing() {
        System.out.println("HelloService is called");
    }
}


在resources/META-INF/services目录下创建com.spitest.IService文件, 并编辑内容为

com.spitest.impl.HelloService
com.spitest.impl.HiService


使用ServicLoader加载实现类:

public class Main {
    public static void main(String[] args) {
        ServiceLoader<IService> services = ServiceLoader.load(IService.class);

        services.forEach( (item) -> {
            item.doSomeThing();  
        });
    }
}
输出:

HelloService is called
HiService is called


查看ServiceLoader源码, 通过Class.forName加载实现类后使用newInstance实例化。



META/services目录名称是写死的, 且目录下的文件必须以类完整名称命名, 因为代码里用getName函数。


可以看出SPI的好处是解耦接口类、实现类, 即实现了热插拔原则, 编译时不需要引用实现类。


在Dubbo源码里定义了SPI注解, 配置文件使用key=value的方式; 凡是使用@SPI的接口都会在/META-INF/dubbo/internal目录里读取实现类。

public @interface SPI {

    /**
     * default extension name
     */
    String value() default "";

}

例如:

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}
对应/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,内容为

dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
   key=value格式的好处是如果某个实现类加载失败, 可以明确的指出来。 SPI.java里注解也做了说明:

If there's third party library referenced by static field or by method in extension implementation, its class will fail to initialize if the third party library doesn't exist. In this case, dubbo cannot figure out extension's id, therefore cannot be able to map the exception information with the extension, if the previous format is used.

有@SPI注解的接口类会依次在下面目录查找

/META-INF/dubbo/internal/

/META-INF/dubbo/

/META-INF/services/

     Dubbo自定义了ExtensionLoader类, 用于替换JDK的ServiceLoader。


   在启动服务时ExtensionLoader会读取3个目录


    可以运行一下dubbo-demo, 并在ExtensionLoader中多打一些断点就可以看出加载过程和参数定义;




猜你喜欢

转载自blog.csdn.net/brycegao321/article/details/79354595