[Java]Java SPI机制

本文详细介绍了JDK SPI(Service Provider Interface)服务加载机制的概念、实践步骤,包括如何定义Service接口、ServiceProvider实现、配置文件编写以及编写测试应用。展示了如何利用SPI进行服务注册和加载,以及扩展使用场景,适合对服务发现感兴趣的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础环境

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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值