SPI + JDK 9 + 模块信息.java

Posted

技术标签:

【中文标题】SPI + JDK 9 + 模块信息.java【英文标题】:SPI + JDK 9 + module-info.java 【发布时间】:2018-03-19 19:42:42 【问题描述】:

我正在 JDK 9 上试验 SPI。整个示例适用于没有“module-info.java”的 JDK 9。添加“module-info.java”后,ServiceLocator 没有找到实现类。我很困惑,在模块化的 JDK 9 项目中找不到有效的 SPI 示例。

所以我的示例项目如下所示:

/spidemo
├── apiModule
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               ├── eu
│               │   └── com
│               │       └── example
│               │           └── text
│               │               └── spi
│               │                   └── TextAPI.java
│               └── module-info.java
├── applicationB
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── eu
│           │       └── com
│           │           └── example
│           │               └── spi
│           │                   └── b
│           │                       └── application
│           │                           └── DemoB.java
│           └── module-info.java
├── applicationCommon
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               ├── eu
│               │   └── com
│               │       └── example
│               │           └── spi
│               │               └── application
│               │                   └── TextAPIProvider.java
│               └── module-info.java
├── implementationB
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── eu
│           │       └── com
│           │           └── example
│           │               └── implb
│           │                   └── text
│           │                       └── TextB.java
│           ├── module-info.java
│           └── resources
│               └── META-INF
│                   └── services
│                       └── eu.com.example.text.spi.TextAPI

我已经介绍了界面:

package eu.com.example.text.spi;
public interface TextAPI 
    String getHelloWorldText();

此接口由以下人员实现:

package eu.com.example.implb.text;
import eu.com.example.text.spi.TextAPI;
public class TextB implements TextAPI  
    public String getHelloWorldText() 
        return "Text from B implementation";
    

通过类似于以下的代码搜索实现:

package eu.com.example.spi.application;
import eu.com.example.text.spi.DefaultTextAPI;
import eu.com.example.text.spi.TextAPI;
import java.util.ServiceLoader;
public class TextAPIProvider 

    public static TextAPI getProvider(String providerName) 
        ServiceLoader<TextAPI> serviceLoader = ServiceLoader.load(TextAPI.class);
        for (TextAPI provider : serviceLoader) 
            String className = provider.getClass().getName();
            if (providerName.equals(className)) 
                return provider;
            
        
        throw new RuntimeException(providerName + " provider is not found!");
    

现在是有趣的部分。当我在没有以下情况下执行课程时:

/implementationB/src/main/java/module-info.java /applicationB/src/main/java/module-info.java

然后找到实现类并打印出文本。

package eu.com.example.spi.b.application;
import eu.com.example.spi.application.TextAPIProvider;
public class DemoB 
    public static void main(String[] args) 
        System.out.println("---> " + TextAPIProvider.getProvider("eu.com.example.implb.text.TextB").getHelloWorldText());
    

引入这两个“module-info.java”文件后,ServiceLocator没有找到实现类。 /applicationB/src/main/java/module-info.java的内容:

module eu.com.example.applicationB 
    requires eu.com.example.apiModule;
    requires transitive eu.com.example.applicationCommon;
    uses eu.com.example.text.spi.TextAPI;

/implementationB/src/main/java/module-info.java的内容:

module eu.com.example.implb.text 
    requires eu.com.example.apiModule;
    exports eu.com.example.implb.text;
//    provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;

当我取消注释时:

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;

行则发生编译错误:

.../implementationB/src/main/java/module-info.java:[7,74] the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
.../implementationB/src/main/java/module-info.java:[7,5] service implementation must be defined in the same module as the provides directive

我曾尝试根据编译错误提示更改包名称,但随后我引入了“拆分包”问题。

如何在完全模块化的 JDK 9 中使用 ServiceLocator?可能吗?有没有人看过工作的例子? 代码也可以看这里:https://github.com/RadoslawOsinski/spidemo

【问题讨论】:

我假设您的意思是添加 provides eu.com.example.text.spi.TextAPI with eu.com.example.implb.text.TextB 因为 TextB 是实现(而不是服务类型)。 非常感谢!我在这个错字上花了几个小时。这解决了我的问题。 【参考方案1】:

你可以改为使用:-

provides eu.com.example.text.spi.TextAPI with eu.com.example.implb.text.TextB; 
// you provide a service through its implementation

而不是

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI; 

Services 文档中提供了围绕实施的示例。

【讨论】:

【参考方案2】:

一个模块可以通过特定类型的服务提供者来指定它提供的服务。它使用provideswith 关键字声明。

语法:提供 serviceType with implementationTypes;

(可以将多个实现类型指定为逗号分隔的列表)

因此在您的模块eu.com.example.implb.text 中应添加以下语句。

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;

注意:provides 不等于 exports。因此,任何其他需要eu.com.example.implb.text 的模块如果不导出,将无法访问eu.com.example.implb.text.TextB

【讨论】:

以上是关于SPI + JDK 9 + 模块信息.java的主要内容,如果未能解决你的问题,请参考以下文章

JDK源码解析之Java的SPI机制

Java SPI 源码解析

01_Jdk自带SPI

其中一个--module-version或--hash-dependencies没有module-info.class错误在jdk 9中打包为jar时

Java 9的JDK中值得期待的:不仅仅是模块化

Dubbo 2.7.3源码分析——JDK SPI篇