介绍 Java Service Provider Interface

大多数应用中我们使用依赖注入让代码保持松耦合,但有时我们仅需要简单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成功加载并创建了对于实例。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值