Java极客  |  作者  /  铿然一叶

这是Java极客的第 81 篇原创文章

相关阅读:

1. 什么是插件

通俗的讲插件有以下特征:

1.增加或者替换已有能力

2.不影响原有功能

3.对原有系统无侵入

例如替换电脑中的内存条和显卡,属于替换原有能力,Intellij Idea增加各种代码检查插件属于增加能力。

2. 实现扩展性的方式和插件的应用场景

插件提供了扩展性,实现扩展性的方式还有很多,例如:

1.模板方法设计模式通过子类实现父类的抽象方法实现扩展

2.方法参数通过传入接口实现扩展

插件和其他扩展方式的差别是:

插件是在已有的软件系统/框架上扩展,引入插件后,系统还是原来的系统,例如Intellij Idea增加了代码检查的插件,还是在使用Intellij Idea这个软件,而上述的其他两种方式则不是在原有系统扩展,而是增加或者改变了某个组件的能力引入到自己的软件系统中。

因此,插件的应用场景为:

1.产品研发团队进行产品化开发,提供通用的产品能力,并提供插件化机制

2.定制团队根据需要扩展插件

3. SPI插件扩展

先看下SPI扩展机制的实现方式:

1.产品研发需要开发一个接口(这里也可以是类),并预埋在系统中

2.产品研发在系统中通过ServiceLoader加载预埋的接口类并调用

3.定制研发开发实现类

4.定制研发在classpath路径的META-INF/services目录下生成一个和接口类的同名文件,同名文件的内容只有一行,为具体的实现类名

在满足以上几点后,如果在运行期发现接口类有子类,则子类会被调用,这样就实现了插件的插入。

4. 代码样例

4.1. 代码结构

原生的SPI机制会查找插件,查找不到则不会处理,在实际项目落地中需要支持默认的插件实现,因此得到如下的结构图:

类职责SServiceLoader负责加载插件类,先通过SPI机制加载定制插件,如果找不到则通过ServiceRegistry获取默认插件

ServiceRegistry负责默认插件注册,和默认插件的查询

DefaultImplClass默认插件

4.2. 代码

4.2.1. ServiceRegistry.java

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

/**

* @ClassName ServiceRegistry

* @Description

* @Author 铿然一叶

* @Date 2021/2/13 18:37

* @Version 1.0

* 掘金:https://juejin.im/user/5d48557d6fb9a06ae071e9b0

**/

public final class ServiceRegistry{

private static Map serviceMap = new ConcurrentHashMap<>();

// 注册默认实现类

static {

register(ICat.class, Ragdoll.class); // 默认值,没有定制

register(IBird.class, Sparrow.class); // 默认值,将被parrot替代

}

// 提供给外部注册用

public static void register(Class interfaceClass, Class defaultImplClass){

serviceMap.put(interfaceClass, defaultImplClass);

}

// 包级访问权限,不需要提供给外部调用

static Class getImplClass(Class interfaceClass) throws Exception{

if (!serviceMap.containsKey(interfaceClass)) {

throw new Exception("没有找到实现类, " + interfaceClass.getName());

}

return serviceMap.get(interfaceClass);

}

}

复制代码

4.2.2. SServiceLoader.java

import java.util.ServiceLoader;

/**

* @ClassName SServiceLoader

* @Description

* @Author 铿然一叶

* @Date 2021/2/13 18:42

* @Version 1.0

* 掘金:https://juejin.im/user/5d48557d6fb9a06ae071e9b0

**/

public class SServiceLoader{

private S s;

// 私有构造器,不需要外部实例化

private SServiceLoader(S s){

this.s = s;

}

public S getService(){

return s;

}

public static SServiceLoader load(Class service) throws Exception{

SServiceLoader ssServiceLoader = null;

ServiceLoader sServiceLoader = ServiceLoader.load(service);

try {

if (sServiceLoader.iterator().hasNext()) {

ssServiceLoader = new SServiceLoader(sServiceLoader.iterator().next());

} else {

Class clazz = ServiceRegistry.getImplClass(service);

ssServiceLoader = new SServiceLoader(clazz.newInstance());

}

} catch (Throwable e) {

throw e;

}

return ssServiceLoader;

}

}

复制代码

4.2.3. 插件相关类

4.2.3.1. IBird.java

public interface IBird{

void say();

}

复制代码

4.2.3.2. Sparrow.java

public class Sparrow implements IBird{

@Override

public void say(){

System.out.println("I am sparrow.");

}

}

复制代码

4.2.3.3. Sparrow.java

public class Parrot implements IBird{

@Override

public void say(){

System.out.println("I am parrot.");

}

}

复制代码

4.2.3.4. ICat.java

public interface ICat{

void say();

}

复制代码

4.2.3.5. Ragdoll.java

public class Ragdoll implements ICat{

@Override

public void say(){

System.out.println("I am ragdoll.");

}

}

复制代码

4.2.3.6. IDog.java

public interface IDog{

void say();

}

复制代码

4.2.3.7. Labrador.java

public class Labrador implements IDog{

@Override

public void say(){

System.out.println("I am labrador.");

}

}

复制代码

4.2.3.8. IFish.java

public interface IFish{

void say();

}

复制代码

4.2.4. 测试类

public class ServiceTest{

public static void main(String[] args) throws Exception{

// 通过SPI扩展

IDog iDog = createIDog();

iDog.say();

// 检查是否单例

IDog iDog1 = createIDog();

System.out.println("iDog: " + iDog.toString());

System.out.println("iDog1: " + iDog1.toString());

// 没有定制,使用默认值

SServiceLoader catServiceLoader = SServiceLoader.load(ICat.class);

ICat iCat = catServiceLoader.getService();

iCat.say();

// 使用定制值覆盖默认值

SServiceLoader birdServiceLoader = SServiceLoader.load(IBird.class);

IBird iBird = birdServiceLoader.getService();

iBird.say();

// 找不到定制和默认实现,抛出异常

SServiceLoader fishServiceLoader = SServiceLoader.load(IFish.class);

IFish iFish = fishServiceLoader.getService();

iFish.say();

}

private static IDog createIDog() throws Exception{

SServiceLoader sServiceLoader = SServiceLoader.load(IDog.class);

return sServiceLoader.getService();

}

}

复制代码

4.2.5. 插件配置文件

插件配置文件需在classpath中的META-INFO/services目录下:

4.2.5.1. com.javageektour.spi.IBird

com.javageektour.spi.Parrot

复制代码

4.2.5.2. com.javageektour.spi.IDog

com.javageektour.spi.Labrador

复制代码

4.2.6. 输出结果

I am labrador.

iDog: com.javageektour.spi.Labrador@4554617c

iDog1: com.javageektour.spi.Labrador@74a14482

I am ragdoll.

I am parrot.

Exception in thread "main" java.lang.Exception: 没有找到实现类, com.javageektour.spi.IFish

at com.javageektour.spi.ServiceRegistry.getImplClass(ServiceRegistry.java:31)

at com.javageektour.spi.SServiceLoader.load(SServiceLoader.java:33)

at com.javageektour.spi.ServiceTest.main(ServiceTest.java:32)

复制代码

5. 总结

1.SPI是Java原生实现的插件机制,原理是通过配置文件动态加载类,在实际落地时,自行实现也可以

2.插件提供了扩展性,是在已有系统中增加或者替换某个能力

3.插件机制对已有系统无侵入

3.插件机制适用于产品团队和定制团队协作开发

java spi 扩展_【扩展和解耦】JAVA原生SPI实现插件扩展相关推荐

  1. java python算法_用Python,Java和C ++示例解释的排序算法

    java python算法 什么是排序算法? (What is a Sorting Algorithm?) Sorting algorithms are a set of instructions t ...

  2. java实现报表_用存储过程和 JAVA 写报表数据源有什么弊端?

    用存储过程和 JAVA 写报表数据源有什么弊端?跟着小编一起来一看一下吧! 我们在报表开发中经常会使用存储过程准备数据,存储过程支持分步计算,可以实现非常复杂的计算逻辑,为报表开发带来便利.所以,报表 ...

  3. java必读书籍_最佳5本Java性能调优书籍–精选,必读

    java必读书籍 为什么Java开发人员应该阅读有关性能调优的书? 当我很久以前第一次面对这个问题时,我以为以后会做,但是我很长一段时间都没有回过头来. 仅当我在用Java编写的任务关键型服务器端财务 ...

  4. java架构师_成为一名Java高级架构师究竟要学哪些东西??

    Java架构师,应该算是一些Java程序员们的一个职业目标了吧.很多码农码了五六年的代码也没能成为架构师.那成为Java架构师要掌握哪些技术呢,总体来说呢,有两方面,一个是基础技术,另一个就是组织能力 ...

  5. java 冻结对象_模式匹配时冻结 - java

    我的程序遇到一个小问题.它似乎冻结了,很可能是由于while循环引起的. 我正在尝试做的是拾取并替换Java注释.因此,在键入块注释时,您将使用/*打开该注释.如果没有结束符(*/),该程序将在5-6 ...

  6. java 7 反射_【7】java 反射详解

    [7]java 反射详解 获取Class对象的方式: 1. Class.forName("全类名"); 将字节码加载进内存,返回Class对象,多用于配置文件,将类名定义在配置文件 ...

  7. python比java容易学_是不是Python比Java更容易学,更容易就业?

    首先,从编程语言本身的语法结构,Java语言更为困难.对初学者来说,学习Java语言的时间成本较高,并且由于Java语言本身有一个相对高度的抽象,你必须想很多实验需要形成自己的Java编程思想完成.J ...

  8. java 判断类型_如何快速入门Java编程学习(干货)

    一.初识Java 1.生活中的程序: 从起床到教室上课的过程 穿衣打扮>起床>洗漱>出宿舍>>吃早餐>到教室 按照特定的顺序去完成某一件事的过程我们叫做生活中的程序 ...

  9. 支持java虚拟主机_为何缺乏支持Java的虚拟主机

    现在很多站长们,都青睐于香港虚拟主机建站.现在常见的是asp和PHP的程序空间很多,但是Java的少之又少,下面小编我给大家聊一聊! 一.香港Java虚拟主机成本高 使用Java程序建站,就必须要用支 ...

  10. 做Java头发少_这35个Java代码优化细节,你用了吗

    链接:https://www.jianshu.com/p/6e472304b5ac 前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有 ...

最新文章

  1. careercup-中等难度 17.5
  2. 算法训练 字符串的展开
  3. 20个常用的Python小技巧
  4. Java随机生成长宽的矩形_java – 将正方形或矩形分解为大量随机大小的正方形或矩形...
  5. 【记录】jenkins 安装及环境配置(二)
  6. python不同时间周期k线_请问期货不同时间级别的k线呈现相反形态怎么判断买卖点?...
  7. 只要沾上婚恋焦虑,她们就王者变青铜
  8. 使得最右边的元素右边框为0
  9. wing ide 3.x 中文设置
  10. 剑指 Offer II 028. 数组中出现次数超过一半的数字
  11. Linux执行fastqc报错Exception in thread “main“ java.awt.HeadlessException: No X11 DISPLAY variable was s
  12. 一图助你搞明白Spring应用上下文初始化流程!
  13. 86版五笔字根表(JPG版)
  14. redis实现原理和应用(redis读书笔记)
  15. UVM 事务级建模TLM 单向/多向通信 端口 FIFO通信
  16. [荐]硕博经验——科研论文阅读与写作实战技巧
  17. unity设置中文版
  18. VFP开发微信、支付宝扫码支付
  19. “富勒”官网软件感染网银木马 360独家拦截 - 卫星杂谈 - 360官方论坛
  20. DecisionTreeClassifier决策树

热门文章

  1. 如何在Excel中用单元格中的单个空格替换多个空格?
  2. Virbox Protector AAB 加固-兼容 Google Play 上架
  3. C++初阶习题(牛客)【7】最小公倍数
  4. 【AGC001E】BBQ Hard(图论,dp)
  5. 计算机密码学学习笔记(二)——Shannon理论
  6. Shannon极限与Nyquist极限
  7. python离线安装第三方包
  8. jenkins安装配置
  9. 《互联网理财一册通》一一1.3 开通网上银行
  10. ICCV2021|你以为这是一个填色模型?其实我是检索模型!