serviceloader java_Java SPI - ServiceLoader 使用简介

问题引入

以前一直想指定一套标准,让别人按照这个标准来实现,并编写好对应的容器。

然后我在代码中动态获取这些实现,让代码运行起来。

类似于写个 windows 环境,让开发者自己开发对应的软件。

困难

如何获取某个接口的实现?

初步方案

和同事讨论,是通过扫描包的 class 的方式。然后判断是否为定制标准的子类。

缺点

觉得很别扭,需要限定死实现类的包名称,而且性能也较差。

SPI 的解决方式

今天在阅读 hibernate-validator 源码时受到了启发。

可以通过 SPI 的方式,更加自然的解决这个问题。

SPI

SPI 是 Service Provider Interfaces 的缩写。

本文简单介绍下如何使用,具体原理,暂时不做深究。

简单实现

文件目录

.

├── java

│ └── com

│ └── github

│ └── houbb

│ └── forname

│ ├── Say.java

│ ├── Sing.java

│ └── impl

│ ├── DefaultSay.java

│ └── DefaultSing.java

└── resources

└── META-INF

└── services

└── com.github.houbb.forname.Say

定义接口和实现

Say.java

/**

*

接口

*

*

 Created: 2018/5/27 上午10:36  

*

 Project: tech-validation  

*

* @author houbinbin

* @version 1.0

* @since JDK 1.7

*/

public interface Say {

/**

* 说

*/

void say();

}

DefaultSay.java

package com.github.houbb.forname.impl;

import com.github.houbb.forname.Say;

/**

*

*

*

 Created: 2018/5/27 上午10:37  

*

 Project: tech-validation  

*

* @author houbinbin

* @version 1.0

* @since JDK 1.7

*/

public class DefaultSay implements Say {

@Override

public void say() {

System.out.println("Default say");

}

}

编写 services 实现指定

在 resources 目录下,创建 META-INF/services 文件夹,以接口全路径名

com.github.houbb.forname.Say 为文件名称,内容为对应的实现类全路径。

如果是多个,就直接换行隔开。

com.github.houbb.forname.Say

com.github.houbb.forname.impl.DefaultSay

测试

SayTest.java

public class SayTest {

@Test

public void spiTest() {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

ServiceLoader loader = ServiceLoader.load(Say.class, classLoader);

for (Say say : loader) {

say.say();

}

}

}

测试结果

Default say

简单总结

Java 中,可以通过 ServiceLoader 类比较方便的找到该类的所有子类实现。

META-INF/services 下的实现指定和实现子类实现完全可以和接口定义完全分开。

麻烦的地方

每次都要手动创建实现指定文件,比较繁琐。

Auto 就为解决这个问题而生。

Auto 版本

jar 的引入

com.google.auto.service

auto-service

1.0-rc4

true

接口和定义

Sing.java

/**

*

接口

*

*

 Created: 2018/5/27 上午10:36  

*

 Project: tech-validation  

*

* @author houbinbin

* @version 1.0

* @since JDK 1.7

*/

public interface Sing {

/**

* 唱歌

*/

void sing();

}

DefaultSing.java

@AutoService(Sing.class)

public class DefaultSing implements Sing {

@Override

public void sing() {

System.out.println("Sing a song...");

}

}

测试

SingTest.java

public class SingTest {

@Test

public void spiTest() {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

ServiceLoader loader = ServiceLoader.load(Sing.class, classLoader);

for (Sing sing : loader) {

sing.sing();

}

}

}

结果

Sing a song...

简单总结

通过 google 的 auto,可以在编译时自动为我们生成对应的接口实现指定文件。在 target 对应的文件下可以看到。

实现原理,也相对简单。通过 java 的编译时注解,生成对应的文件即可。

项目源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值