• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

Java SPI详细的例子

开发技术 开发技术 2周前 (09-14) 20次浏览

 

先翻一个来自于Baeldung的介绍:

为了更通俗易懂我就没有直译,如果有不严谨的地方请大神指教。

JavaSPI的定义

Java SPI defines four main components  

SPI四个主要概念

1. Service

 服务

A well-known set of programming interfaces and classes that provide access to some specific application functionality or feature.

服务是一组接口类,用来定义某些功能或特性的接口(常常是某个jar包的功能或特性),暴露给其他程序调用。

2. Service Provider Interface

 服务提供者接口

An interface or abstract class that acts as a proxy or an endpoint to the service.

If the service is one interface, then it is the same as a service provider interface.

Service and SPI together are well-known in the Java Ecosystem as API.

一个接口或抽象类,用来代理一个服务端。

如果是一个接口服务,那么这就和一个服务提供者接口一样。

服务和SPI一起便是java生态中的API了。

还是来一段自己的理解吧。。。就是定义接口,用来被别人去实现

服务提供者接口 – 如果我们有一个MyService的接口,我们把接口打进jar包,留给别人去依赖并实现。那这个接口就是服务提供者的接口。我们简单的理解成这个jar是服务接口提供者。

没错,我们就是服务提供者。我们制定规则,定义接口。接下来留个各个厂商去实现各个接口。

比如JDBC,Java开发者把链接数据库的方法写成接口。这个接口只要传进去url name passwrod 就能链接DB了。这就是一个SPI

当然很重要一步的是要由每种不同的DB厂商去实现具体怎么用这么几个简单的参数去实现链接。厂商把实现打个jar包(比如:Oracle的ojdbc-xx.jar),开发者把他加到classpath中就可以链接数据库了。

3. Service Provider

 服务提供者

A specific implementation of the SPI. The Service Provider contains one or more concrete classes that implement or extends the service type.

A Service Provider is configured and identified through a provider configuration file which we put in the resource directory META-INF/services. The file name is the fully-qualified name of the SPI and his content is the fully-qualified name of the SPI implementation.

The Service Provider is installed in the form of extensions, a jar file which we place in the application classpath, the java extension classpath or user-defined classpath.

不需要翻译了,简单的理解就是接口的实现,请看上面#2中的例子。Oracle开发的链接数据库的jar就是一个服务提供者。

4. ServiceLoader

At the heart of the SPI is the ServiceLoader class. This has the role of discovering and loading implementations lazily. It uses the context classpath to locate providers implementations and put them in an internal cache.

这个类ServiceLoader是SPI的核心。使用懒加载方式发现和加载服务的实现。他通过classpath的定位服务的实现,并将他们加载到jvm中。

举个例子

创建一个项目:SpiDemo(服务接口)

定义服务接口

package com.ian.demo.spi;

public interface MyService {
    void sayHello();
}

打包并install安装到本地 .m2的路径下,pom.xml:

<groupId>com.ian.demo</groupId>
 <artifactId>SpiDemo</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>

另起一个新的项目:SpiDemoServicesProvider(服务提供者)

这里注意下,我们需一个META-INF来暴露我们的接口实现。

并在其中添加一个以接口包命名的文件

Java SPI详细的例子

 

 

 里面写上我们的实现类:

com.ian.demo.spi.provider.MyServiceImpl

导入刚才install的jar包,pom.xml:

    <dependencies>
        <dependency>
            <groupId>com.ian.demo</groupId>
            <artifactId>SpiDemo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

实现服务接口:

public class MyServiceImpl implements MyService {
    @Override
    public void sayHello() {
        System.out.println("Hi, There!");
    }
}

打包安装到本地m2目录:

    <groupId>com.ian.demo</groupId>
    <artifactId>SpiDemoServicesProvider</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

请注意,我们需要把SPI实现通过META-INF暴露出去,pom中添加:
        </resources>
            <resource>
                <directory>META-INF</directory>
                <targetPath>/META-INF/</targetPath>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>

 

另起一个项目调用接口:接口真正的使用者

main函数,ClassLoader加载接口实现

public class Main {
    public static void main(String[] args) {
        ServiceLoader<MyService> myServiceImplSet = ServiceLoader.load(MyService.class);
        if(myServiceImplSet.iterator().hasNext()){
            myServiceImplSet.iterator().next().sayHello();
        }
    }
}

导入上面的SPI和SPI实现jar:

<dependencies>
    <dependency>
        <groupId>com.ian.demo</groupId>
        <artifactId>SpiDemoServicesProvider</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
  <dependency>
      <groupId>com.ian.demo</groupId>
      <artifactId>SpiDemo</artifactId>
      <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>

运行:

Hi, There!
Process finished
with exit code 0

总结:

是不是很累赘,为啥要写三个项目?一个不就行了?还要用ServiceLoader是不是在故弄玄虚。。

当然不是。使用场景就如我上面Oracle的例子。咱们普通开发者要给公司做个项目,需要连接Oracle数据库,好在java给我们提供了连接接口,并且Oracle提供了实现,我们只要有jre和ojdbc的jar就可以了。有了SPI后,java和Oracle’的开发者就可以分工合作了。这是java典型的解构和强封装的特性。我们日常的开发中如果有多个系统合作开发的也可以这样进行。

 


程序员灯塔 , 版权所有
转载请注明原文链接:https://www.wangt.cc/2020/09/java-spi%e8%af%a6%e7%bb%86%e7%9a%84%e4%be%8b%e5%ad%90/
喜欢 (0)