Java SPI机制

摘要:
老大看到我的代码,直接让用Javaspi机制去做。javaspi就是提供这样的一个机制:为某个接口寻找服务实现的机制。在Maven工程的src/main/resources/下,创建META-INF/services/com.A.standard.chain.LogProcessHandler文件里面具体内容:com.A.tools.BasicParamProcessHandlercom.A.tools.CommonParamProcessHandlercom.A.tools.TestParamProcessHandler主类中初始化/***初始化具体的处理类*/privatevoidinitConcreteHandler(){//初始化handlers=newArrayList();//通过javaspi机制load所有处理的handerServiceLoaderloader=ServiceLoader.load;for{handlers.add;}//handler排序Collections.sort;}Ps:一般情况下,是按照文件中实现类顺序加载类,但是可能出现特殊情况。

最近在一个日志标准化的项目中,使用了责任链模式来链接每一个具体的处理Handler.但是在实例化时,需要每一个都去创建实例。
如:

/**
 * 初始化具体的处理类
 */
private void initConcreteHandler() {
    handlers.add(new BasicParamHandler());
    handlers.add(new CommonParamHandler());
    handlers.add(new TestParamHandler());
    ……
}

这种,涉及了具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。老大看到我的代码,直接让用Java spi机制去做。

1. SPI机制简介

Service Provider Interface:服务提供者接口.例如,系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。

面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。在模块化设计中这个机制尤其重要。

java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。

2. 实现案例

1.common-logging

apache最早提供的日志的门面接口。只有接口,没有实现。具体方案由各提供商实现, 发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通过读取该文件的内容找到日志提工商实现类。只要我们的日志实现里包含了这个文件,并在文件里制定 LogFactory工厂接口的实现类即可。

2. JDBC

jdbc4.0以前, 开发人员还需要基于Class.forName("xxx")的方式来装载驱动。
创建连接:

DriverManage.getConnection()中,有Connection con = aDriver.driver.connect(url, info);

driver成员变量,是java.sql.Driver接口,Java对外公开的一个加载驱动接口,Java并未实现,至于实现这个接口由各个Jdbc厂商去实现。

如MySQL,mysql-connector-java-5.1.38.jar包下面META-INF.services包下有个java.sql.Driver文件打开文件有下面两行
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

3. 具体实现

(1)如,我们这里的处理模块。面向接口编程。这是我的一种实现方式。

public interface LogProcessHandler {
/**
 * 解析日志,具体的给Bean赋值的逻辑
 * @param context 上下文
 */
void process(ProcessorContext context);
/**
 * 日志解析handler拓扑顺序,从小到大排列
 */
int order();
}

(2)每一个具体实现类,如BasicParamHandler、CommonParamHandler都实现了这个接口,里面具体的process()方法。

(3)在Maven工程的src/main/resources/下,创建META-INF/services/com.A.standard.chain.LogProcessHandler文件(UTF-8)
里面具体内容:

com.A.tools.BasicParamProcessHandler
com.A.tools.CommonParamProcessHandler
com.A.tools.TestParamProcessHandler

(4)主类中初始化

 /**
 * 初始化具体的处理类
 */
private void initConcreteHandler() {
    // 初始化
    handlers = new ArrayList<>();
    // 通过java spi机制load所有处理的hander
    ServiceLoader<LogProcessHandler> loader = ServiceLoader.load(LogProcessHandler.class);
    for (LogProcessHandler hander : loader ) {
        handlers.add(hander);
    }
    // handler 排序
    Collections.sort(handlers, new Comparator<LogProcessHandler>() {
        @Override
        public int compare(LogProcessHandler o1, LogProcessHandler o2) {
            return o1.order() - o2.order();
        }
    });
}

Ps:

一般情况下,是按照文件中实现类顺序加载类,但是可能出现特殊情况。因此对集合中的实现类进行排序,这也是接口中定义了order方法的原因。

4. 原理

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。

当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。

from:https://www.cnblogs.com/wangrd/p/7090918.html

免责声明:文章转载自《Java SPI机制》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇超级详细的Maven使用教程Qt Quick之TableView的使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Dubbo-深入配置

一、dubbo注解:   提供端暴露服务时与消费端调用远程接口可以使用注解形式配置   》服务端: 》1.原来采用接口配置,暴露服务,ref:指向真正的实现对象 <dubbo:service interface="com.ll.service.UserService" ref="userServiceImpl" /> 》2.现在采用注解...

支付宝异步通知处理实现原理总结

支付宝有一个接口:实现支付请求(里面要提供一个订单号) 你有一个接口:实现支付结果的通知(通知里面会包含订单号) 后面就简单了 1)你生成订单 请求调用 支付宝接口 去支付(然后。。然后就没然后了) 2)你的接口:等着呗 支付宝会调用的接口 通知你 那个订单完成的结果 3) 支付系统的异步通知实质上是给给定的地址发送请求实现的,这个地址很可能是不会有页面而...

Java的RMI远程方法调用实现和应用

最近在学习Dubbo,RMI是很重要的底层机制,RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机(即JVM虚拟机)上的对象可以调用另外一台计算机上的对象来获取远程数据。 RMI的实现对建立分布式Java应用程序至关重要,是Java体系非常重要的底...

接口测试人员需要掌握的知识技能

一、首先明白接口是什么 软件接口是指程序中具体负责在不同模块之间传输或接受数据的并做处理的类或者函数。(而不是指传输的数据!!) 二、什么是接口测试 接口测试就是通过向接口传递数据来测试这个接口是否正确。比如:一个QQ登录功能接口,就需要我们传递QQ号和密码去验证这个登录接口是否正确,能否使用。 三、进行接口测试需要掌握哪些知识 1、了解系统及内部各个组件...

python的接口和抽象类

抽象基类有些面向对象的语言,如JAVA,支持接口,可以声明一个支持给定的一些方法方法,或者支持给定存取协议的类。抽象基类(或者ABCs)是Python里一个相同的特性。抽象基类由abc模块构成,包含了一个叫做ABCMeta的metaclass。这个metaclass由内置的isinstance()和issubclass()特别处理,并包含一批会被Pytho...

java~jackson实现接口的反序列化

jackson是springboot中集成的序列化方式,是默认的json序列化方式,当然你可以使用其它的序列化工具代替它,不过今天我们还是说一下它,使用jackson进行序列化一个类,然后再把它的JSON字符反序列化为它的接口对象。 现实 这种方式默认是不行的,因为接口不能被自动实例化 使用redisTelmplete时,如果使用objectMapper...