Skip to main content
 首页 » 编程设计

介绍 Java Service Provider Interface

2022年07月19日130kevingrace

大多数应用中我们使用依赖注入让代码保持松耦合,但有时我们仅需要简单DI,而不是其他较重的框架,如Spring ,Google Guice 等,它们会让jar包非常大,增加了很多不必要的类。Java 自身也提供了 ServiceLoader 类 ,用于运行时注入依赖。

ServiceLoader 自JDK 3引入仅作内部使用,到JDK6,该类范围为public,但仍然是final类 —— 我们不能扩展其功能。ServiceLoader 在 JDK 9 中扮演重要角色。本文通过示例给你展示如何使用 ServiceLoader 。

示例

需要 JDK >= 6 , 使用maven 进行打包项目生成jar文件。

定义服务抽象接口

创建服务的抽象层:

package com.dataz.service; 
 
public interface GreetingsService {
    
    /** 
     * greeting to name 
     * @param name name 
     */ 
    void sayHello(String name); 
} 

我们定义两个服务实现类:

package com.dataz.service.impl; 
 
import com.dataz.service.GreetingsService; 
 
public class ConsoleGreetings implements GreetingsService {
    
    @Override 
    public void sayHello(String name) {
    
        System.out.println("Hello to " + name + " on console."); 
    } 
} 
package com.dataz.service.impl; 
 
import com.dataz.service.GreetingsService; 
 
public class EmailGreetings implements GreetingsService {
    
    @Override 
    public void sayHello(String name) {
    
        System.out.println("Hello to " + name + " by email."); 
    } 
} 

定义 Provider

现在我们需要定义提供者,其负责在运行时载入服务实现并创建相应实例。

package com.dataz.provider; 
 
import com.dataz.service.GreetingsService; 
import java.util.NoSuchElementException; 
import java.util.ServiceLoader; 
 
public class GreetingsProvider {
    
    private static GreetingsProvider provider; 
    private ServiceLoader<GreetingsService> loader; 
 
    private GreetingsProvider() {
    
        loader = ServiceLoader.load(GreetingsService.class); 
    } 
 
    public static GreetingsProvider getInstance() {
    
        if(provider == null) {
    
            provider = new GreetingsProvider(); 
        } 
 
        return provider; 
    } 
 
    public void greeting(String greetingType, String name){
    
        loader.iterator().forEachRemaining(s -> {
    
            if (s.getClass().getName().contains(greetingType)){
    
                s.sayHello(name); 
            } 
        }); 
    } 
 
    public GreetingsService serviceImpl() {
    
        GreetingsService service = loader.iterator().next(); 
 
        if(service != null) {
    
            return service; 
        } else {
    
            throw new NoSuchElementException("No implementation for GreetingsProvider"); 
        } 
    } 
} 

同时定义了两个方法用于测试,greeting 方法根据名称判断调用那个实现,serviceImpl 仅返回一个具体实现。实际应用中可根据业务实现更有意义的逻辑,这里仅为演示。

创建目录

resources 目录下(类路径) 创建目录 META-INF/services ,并在该目录下创建文件 com.dataz.service.GreetingsService ,该文件名正是服务接口的全路径名称。文件内容包括所有的实现类:

com.dataz.service.impl.ConsoleGreetings 
com.dataz.service.impl.EmailGreetings 

测试

package com.dataz.main; 
 
import com.dataz.provider.GreetingsProvider; 
import com.dataz.service.GreetingsService; 
 
public class Launcher {
    
    public static void main(String... args) {
    
        try{
    
            GreetingsProvider provider = GreetingsProvider.getInstance(); 
            GreetingsService service = provider.serviceImpl(); 
 
            service.sayHello("tom"); 
            provider.greeting("Email", "jack"); 
        }catch (Exception e){
    
            e.printStackTrace(); 
        } 
    } 
} 

输出内容:

Hello to tom on console. 
Hello to jack by email. 

输出与期望一致,provider成功加载并创建了对于实例。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/118339081
阅读延展