栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

使用jdk的spi机制实现接口的扩展和解耦

使用jdk的spi机制实现接口的扩展和解耦

0、背景

总所周知,SPI在很多地方都有着很好的实践,比如JDBC驱动的加载、dubbo等,SpringBoot项目的autoConfiguration也是类似的原理。这里就感觉用来做模块解耦也不错。比如我们做应用管理的一个服务,它具有应用安装、升级、扩容等功能,经常在安装、升级、扩容流程中应用要求做一些定制化的东西,比如安装数据库,需要初始化database;安装kafka,需要初始化topic;对kafka的消费者进行扩容,需要同步扩topic的分区等等。而对于做应用管理的服务来说,它不应该区别化的对待这些应用,否则随着时间的发展,这个服务就会变得越来越复杂,难以与上层应用解耦。

这时候我们可以使用SPI的机制,为每一个支持扩展的流程定义一个扩展接口,交给上层应用去实现,使用SPI来加载这些实现类,从而实现让应用在安装、升级、扩容流程中加入自己个性化的功能。
对于应用管理服务和应用自身都有好处:

于应用管理服务而言,只需要维护扩展接口,无需理解应用的逻辑,无需维护应用的个性化定制功能于上层应用而言,无需理解应用管理服务的逻辑,需要扩展就自己去实现接口,自己维护定制化的流程代码,当不需要的定制时候直接删除实现即可。 1、先玩一玩SPI吧 1.1 定义一个接口

新建一个module,名为service-spi,新增如下的接口:

package com.example.service.spi;

public interface MyServiceSpi {
    int order();
    void service();
}

这个接口中有两个方法,service是让应用自己去实现的扩展,order用来控制多个实现类的顺序,万一多个应用之间有顺序要求呢,可用这个order来控制。

1.2 定义第一个实现类

新建一个module名为service-impl-one,pom.xml中引用service-spi,新增实现类:

package com.example.service.imp.one;

import com.example.service.spi.MyServiceSpi;

public class ServiceImpleOne implements MyServiceSpi {

    public int order() {
        return 0;
    }

    public void service() {
        System.out.println("my service one");
    }
}

在resources中增加文件夹meta-INF/services,新建一个名为com.example.service.spi.MyServiceSpi的文件,与接口的全路径一致
文件内容为com.example.service.imp.one.ServiceImpleOne

1.3 定义第二个实现类

新建一个module名为service-impl-two,pom.xml中引用service-spi,新增实现类:

package com.example.service.imp.two;

import com.example.service.spi.MyServiceSpi;

public class ServiceImplTwo implements MyServiceSpi {
    public int order() {
        return 1;
    }

    public void service() {
        System.out.println("my service two");
    }
}

在resources中增加文件夹meta-INF/services,新建一个名为com.example.service.spi.MyServiceSpi的文件,与接口的全路径一致
文件内容为com.example.service.imp.two.ServiceImplTwo

1.4 调用MyServiceSpi

调用的module引用

        
            org.example
            service-impl-one
            1.0-SNAPSHOT
        
        
            org.example
            service-imp-two
            1.0-SNAPSHOT
        

通过ServiceLoader获取所有实现类并排序后依次调用

        ServiceLoader loader = ServiceLoader.load(MyServiceSpi.class);
        Iterator iterator = loader.iterator();
        List myServiceSpiList = new ArrayList<>();
        while(iterator.hasNext()){
            myServiceSpiList.add(iterator.next());
        }
        // 根据order进行排序,order值小的先执行
        Collections.sort(myServiceSpiList, new Comparator() {
            @Override
            public int compare(MyServiceSpi o1, MyServiceSpi o2) {
                return o1.order()- o2.order();
            }
        });
        // 串行调用所有实现类
        for (MyServiceSpi serviceSpi:myServiceSpiList) {
            serviceSpi.service();
        }

运行后结果
my service one
my service two

2、思考

调用扩展接口地方是不需要关心接口到底有没有实现类,有多少个实现类,这达到我们想要的模块之间解耦。但是,发现没有,提供接口的jar要和实现的jar跑在同一个jvm里面,这就意味着要么提前把扩展jar包全部打到我们的应用管理服务中,要么支持在单独安装或者升级时动态加载扩展jar,然后reload重新加载实现类。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/744957.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号