首页 > 编程学习 > [Java]Java SPI机制

[Java]Java SPI机制

发布时间:2022/11/10 7:53:45

目录

  • 基础环境
  • 概念
  • 实践
    • SPI规范
    • SPI设计思想
    • 代码实践
      • 项目结构:
      • 定义Serivece
      • 定义Serivece Provider
        • 接口实现类(定义三个)
        • 抽象类实现类
        • 子类重写
      • 编写测试应用
      • 执行结果
  • 扩展使用
  • 代码地址

基础环境

JDK 1.8

概念

SPI(Service Provider Interface): 服务提供接口,是JDK提供的一种服务加载机制,简单理解就是通过此JDK规范可实现服务的发现并加载。主要有三部分组成:
Service: 服务,更准确来说是服务定义,可以接口、抽象类,同样可以是普通的类(服务提供者为其子类,不过一般不这么来定义)
Service Provider: 服务提供者,可以是Service接口实现类,Service抽象类的实现,以及Service类的继承
ServiceLoader: 一个简单的服务提供者加载工具

原始的参考资料可参考JDK源码中对java.util.ServiceLoader的描述。
在这里插入图片描述

实践

SPI规范

  1. 配置文件路径: Service Provider声明配置文件必须在META-INF/services 目录下;
  2. 配置文件名称: Service接口、实现类等全限定类名
  3. 配置文件内容:Service实现类(一个实现类一行)
  4. 保证实现类jar包被正确加载(放置classpath中或自定义Class Loader保证加载)
  5. 实现类提供无参构造方法
    参考示例:
    在这里插入图片描述

SPI设计思想

面向接口编程、接口隔离原则(ISP)

代码实践

项目结构:

在这里插入图片描述
在这里插入图片描述

定义Serivece

  1. 接口cn.com.demo.api.ISpiDemo
package cn.com.demo.api;

public interface ISpiDemo {
    String name();
    String exec(String msg);
}


  1. 抽象类 cn.com.demo.api.AnstractSpiDemo
package cn.com.demo.api;

public abstract class AnstractSpiDemo {
   public abstract String sayHello();
}

  1. 普通类 cn.com.demo.api.SpiDemo
package cn.com.demo.api;

public class SpiDemo {
   public String sayHello(){
       return this.getClass().getSimpleName();
   }
}

定义Serivece Provider

接口实现类(定义三个)

cn.com.demo.impl.Hello0ISpiDemo

package cn.com.demo.impl;

import cn.com.demo.api.ISpiDemo;

public class Hello0ISpiDemo implements ISpiDemo {
   @Override
   public String name() {
       return this.getClass().getSimpleName();
   }

   @Override
   public String exec(String msg) {
       return name()+msg;
   }
}

cn.com.demo.impl.Hello1ISpiDemo

package cn.com.demo.impl;

import cn.com.demo.api.ISpiDemo;

public class Hello1ISpiDemo implements ISpiDemo {
  @Override
  public String name() {
      return this.getClass().getSimpleName();
  }

  @Override
  public String exec(String msg) {
      return name()+msg;
  }
}

cn.com.demo.impl.Hello2ISpiDemo

package cn.com.demo.impl;

import cn.com.demo.api.ISpiDemo;

public class Hello2ISpiDemo implements ISpiDemo {
  @Override
  public String name() {
      return this.getClass().getSimpleName();
  }

  @Override
  public String exec(String msg) {
      return name()+msg;
  }
}

META-INF/services/下定义
文件名:cn.com.demo.api.ISpiDemo
文件内容:

cn.com.demo.impl.Hello1ISpiDemo
cn.com.demo.impl.Hello2ISpiDemo

在这里插入图片描述
注意: 这里的cn.com.demo.impl.Hello0ISpiDemo未被定义到services文件中,应用将不会注册此类。

抽象类实现类

cn.com.demo.impl.AnstractSpiDemoImpl

package cn.com.demo.impl;

import cn.com.demo.api.AnstractSpiDemo;

public class AnstractSpiDemoImpl extends AnstractSpiDemo {
   @Override
   public String sayHello() {
       return this.getClass().getSimpleName();
   }
}

META-INF/services/下定义
文件名:cn.com.demo.api.AnstractSpiDemo
文件内容:

cn.com.demo.impl.AnstractSpiDemoImpl

在这里插入图片描述

子类重写

cn.com.demo.impl.SpiDemoChild

package cn.com.demo.impl;

import cn.com.demo.api.SpiDemo;

public class SpiDemoChild extends SpiDemo {
    @Override
    public String sayHello() {
        return  this.getClass().getSimpleName();
    }
}

META-INF/services/下定义
文件名:cn.com.demo.api.SpiDemo
文件内容:

cn.com.demo.impl.SpiDemoChild

在这里插入图片描述

编写测试应用

cn.com.demo.app.SpiTest

package cn.com.demo.app;

import cn.com.demo.api.ISpiDemo;
import cn.com.demo.api.SpiDemo;
import cn.com.demo.api.AnstractSpiDemo;
import lombok.extern.java.Log;

import java.util.ServiceLoader;

@Log
public class SpiTest {
    public static void main(String[] args) {
        ServiceLoader<ISpiDemo> load = ServiceLoader.load(ISpiDemo.class);
        for (ISpiDemo spiDemo : load) {
            log.info(spiDemo.name()+"-"+spiDemo);
        }
        log.info("------------------------------");
        ServiceLoader<AnstractSpiDemo> loader = ServiceLoader.load(AnstractSpiDemo.class);
        for (AnstractSpiDemo spiDemoC : loader) {
            log.info(spiDemoC.sayHello());
        }
        log.info("------------------------------");
        ServiceLoader<SpiDemo> loader1 = ServiceLoader.load(SpiDemo.class);
        for (SpiDemo spiDemo : loader1) {
            log.info(spiDemo.sayHello());
        }

    }
}

执行结果

在这里插入图片描述

扩展使用

  1. 利用spi的加载机制,在Class Loader加载和实例化时,可在静态代码块、非静态代码块、无参构造等处做服务的初始化工作,如JDBC4.0规范之后不再需要Class.forName()来注册驱动就是因为在静态代码块中已经完成了DriverManager.registerDriver(new Driver())。

代码地址

spidemo:https://gitcode.net/master336/spidemo/

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式