Java SPI - Go语言中文社区

Java SPI


Java SPI的全称是Java Service Provider Interface ,翻译成中文的意思就服务提供接口。它是为了解决接口实现方与调用方分离的。
在面向对象的设计里我们一般推荐面向接口编程。

image

在多个模块之间不对实现进行硬编码,如果替换一种实现实现方式就会重新修改代码。可能有人会说Spring 支持一个接口多个实现类。在注入Bean的时候只要使用@Qualifier注解指明我们所要使用的实现类不就可以了吗?当然这么做是没有错的!但是并不是所有的软件产品依赖了Spring。这时候我们如果硬编码就会无法做到很好的扩展性,比如你要写一个Rpc的矿建,对于它的序列化方案,你肯定不想只使用一种方式,但是如果有一天有几个新的第三方想要扩展新的序列化方案。你肯定也不想它修改你的原有代码,重新实现接口然后创建实例,下次要换别的你就又要修改代码。

如果我的Rpc框架不用关心别人怎么实现的序列化的只是调用接口就可以,无需关注别人的实现,那就不用总是改代码了。这时候大家就会想:没有实现类怎么调用方法,Java 中的方法的调用都必须实例化。也许这时候有人会说:JDK的动态代理不就可以创建一个接口的实现类. 但是你别忘了它还是依赖一个真实对象的引用。

public class AnimalInvocationHandle<T> implements InvocationHandler {
    
    private Object realObject;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object object = method.invoke(realObject,args);
        return  object;
    }
}


newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

在jdk6里面引进的一个新的特性ServiceLoader,而且ServiceLoader可以通过service provider的配置文件来装载指定的service provider。当服务的提供者,提供了服务接口的一种实现之后,我们只需要在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

Spi Hello World

下面我们就用一个例子来展开
我们定义了一个接口HelloService,然后再resources目录下创建META-INF/services/目录然后创建一个名为我们Service注解全限定名文件:
com.clown.spi.HelloService 内容暂时为空。

public interface HelloService {
    void printHello();
}

打印Hello 我们这里呢实现两个接口一个打印中文的Hello 也就是“你好”,另一个打印英文的“Hello”

public class SayChineseHello implements HelloService {
    @Override
    public void printHello() {
        System.out.println("你好");
    }
}


public class SayEnglishHello implements HelloService {
    @Override
    public void printHello() {
        System.out.println("hello");
    }
}

我们通过ServiceLoad去调用它:

public class SpiTest {
    public static void main(String[] args) {
        ServiceLoader<HelloService> serviceLoader= ServiceLoader.load(HelloService.class);
        Iterator<HelloService> iterator = serviceLoader.iterator();
        while (iterator.hasNext()){
            iterator.next().printHello();
        }
    }
}

这里呢 ,我们还不知道我们实现是哪一个,会打印什么内容。调用方完全不知道底层的实现细节。接下来我们来指定我们所调用的实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aiYyWuii-1579244431632)(E5AC2B1D01454C0E878769EB4DFF39EF)]

运行代码,打印结果如下:

你好

SPI的运用

Servlet3.0 规范

ServletContainerInitializer 类通过jar services API 查找。对于每一个应用,应用启动时,由容器创建一个
ServletContainerInitializer 实例。框架提供的ServletContainerInitializer 实现必须绑定在jar 包的
META-INF/services 目录中的一个叫做javax.servlet.ServletContainerInitializer的文件,根据jar services API,
指定ServletContainerInitializer 的实现。

这部分的内容: 可以看我 ServletContainerInitializer-- Servlet3.1 无XML启动SpringMVC

Dubbo

Dubbo对自己对SPI 进行了优化,而且提供了注解@SPI等,对SPI的支持。详见Dubbo源码,《深入理解Apache Dubbo与实战》

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_24532581/article/details/104019538
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢