首页 > 其他分享 >jdk的SPI(Service Provider Interface)

jdk的SPI(Service Provider Interface)

时间:2024-04-01 16:01:38浏览次数:15  
标签:return Service service 接口 ServiceLoader SPI Interface null public

1、定义

  SPI是Java提供的一种服务发现机制,用于在运行时动态查找和加载实现特定接口的服务提供商。按照字面的意思是服务提供接口

将接口与具体业务独立开来。实现调用方与实现方解耦。

1.1API与SPI

  最简单的区别就是接口的属于哪一方,API接口属于实现方,SPI接口属于调用方,SPI是调用方定义接口,由实现方去实现接口。

API是实现方定义接口,由实现方实现接口。

2、实现

  2.1 定义 服务提供者接口

    public interface LoggerService {
        void log(String message);
    }

      2.2 实现方实现这个接口

  public class ConsoleLoggerService implements LoggerService {
        @Override
        public void log(String message) {
            System.out.println("Console Logger: " + message);
        }
    }

    public class FileLoggerService implements LoggerService {
        @Override
        public void log(String message) {
            // 假设将日志写入到文件中...
            System.out.println("File Logger: " + message); // 这里为了演示简化为输出到控制台
        }
    }

   2.3 调用

    public void testFun20() throws UnsupportedEncodingException {

        ServiceLoader<LoggerService> loggerServices = ServiceLoader.load(LoggerService.class);
        for (LoggerService loggerService : loggerServices) {
            loggerService.log("Hello, SPI!");
        }
    }

 

3、注册

  实现服务接口的类需要在自身的JAR包的 META-INF/services 目录下创建一个文件,该文件的名字应为接口的全限定名(即包含完整包路径的类名)。文件内容则为服务提供者的全限定名,每一行对应一个实现类。

com.mvc.testdemo2.dynamic.ConsoleLoggerService
com.mvc.testdemo2.dynamic.FileLoggerService


  原理Java 中的 SPI 机制就是在每次类加载的时候会先去找到 class 相对目录下的 META-INF 文件夹下的 services 文件夹下的文件,将这个文件夹下面的所有文件先加载到内存中,然后根据这些文件的文件名和里面的文件内容找到相应接口的具体实现类,找到实现类后就可以通过反射去生成对应的对象,保存在一个 list 列表里面,所以可以通过迭代或者遍历的方式拿到对应的实例对象,生成不同的实现。

所以会提出一些规范要求:文件名一定要是接口的全类名,然后里面的内容一定要是实现类的全类名,实现类可以有多个,直接换行就好了,多个实现类的时候,会一个一个的迭代加载

4、原理

  在客户端代码中,使用 java.util.ServiceLoader 类的 load(Class<T> service) 方法来加载指定接口的所有服务提供者。这个方法会查找当前类加载器可以访问的所有 JAR 包下的 META-INF/services 目录下的对应接口的服务提供者列表文件

  4.1实现方法

//生成ServerLoader
public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
  //new一个ServerLoader对象
  public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){
      return new ServiceLoader<>(service, loader);
  }
    //初始化构造函数     private ServiceLoader(Class<S> svc, ClassLoader cl) {
      service = Objects.requireNonNull(svc, "Service interface cannot be null");
      loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
      acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
      reload();
    }   //加载     public void reload() {
      providers.clear();
      lookupIterator = new LazyIterator(service, loader);
}

 

5、反射与实例化

  5.1方法迭代

  首先会在 ServiceLoader 的 Provider 缓存中进行查找,如果缓存中没有命中那么则在 LazyIterator 中进行查找

 public Iterator<S> iterator() {
        return new Iterator<S>() {

            Iterator<Map.Entry<String,S>> knownProviders
                = providers.entrySet().iterator();
        
            public boolean hasNext() {
          //先从knowProviders中查询 if (knownProviders.hasNext()) return true;
           //没有查询 LazyIterator中查询
return lookupIterator.hasNext(); } public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } }; }

   5.2lookupIterator迭代

     public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
    
      private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

 

6、迭代使用服务

  客户端可以通过遍历 ServiceLoader 返回的迭代器来依次使用每一个服务提供者的实例,这样就可以灵活地在运行时选择和切换不同的服务实现

   public void testFun20() throws UnsupportedEncodingException {

        ServiceLoader<LoggerService> loggerServices = ServiceLoader.load(LoggerService.class);
        for (LoggerService loggerService : loggerServices) {
            loggerService.log("Hello, SPI!");
        }
    }

 

标签:return,Service,service,接口,ServiceLoader,SPI,Interface,null,public
From: https://www.cnblogs.com/lqh969696/p/18108310

相关文章

  • oop_promax_abstractAndInterface
    abstract/*!!!抽象类、抽象方法是什么样的?1.都是用abstract修饰的;抽象方法只有方法签名,不能写方法体。!!!抽象类有哪些注意事项和特点?1.抽象类中可以不写抽象方法、但有抽象方法的类一定是抽象类。2.类有的成员(成员变量、方法、构造器)抽象类都具备。3.抽象类不......
  • SPI通信协议详解
    文章目录介绍SPI硬件电路移位示意图SPI时序开始与结束时序单元交换字节时序单元模式0(最常用)模式1模式2模式3发送时序指定地址写指定地址读介绍SPISPI由时钟线、主机发送线、主机接收线、从机选择线(一个或多个)组成,拥有高速的速率,使用比较简单,但是需要的线更多,更容......
  • Android Context 获取getSystemService全流程分析
    1. ActivityManager的获取ActivityManagermActivityManager=(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);2.在ContextImpl.getSystemService->ActivityManager3.在SystemServiceRegistry中调用getSystemSrevice//缓存//注册//静......
  • flutter加载网络图片错误EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE The following
    在flutter里使用image.network加载网络图片遇到错误══╡EXCEPTIONCAUGHTBYIMAGERESOURCESERVICE╞════════════════════════════════════════════════════ThefollowingSocketExceptionwasthrownresolvingani......
  • 每日面经分享(Spring Boot: part2 Service层)
    SpringBootService层的作用a.封装业务逻辑:Service层负责封装应用程序的业务逻辑。Service层是控制器(Controller)和数据访问对象(DAO)之间的中间层,负责处理业务规则和业务流程。通过将业务逻辑封装在Service层中,可以保持控制器的简洁性,提高代码的可维护性和可复用性。b.事......
  • Android基于MediaBroswerService的App实现概述,android零基础入门
    谷歌官方提供了MediaBroswerService,通过其可以帮助我们实现上述的需求。MediaBroswerServiceAndroid多媒体架构Android多媒体播放采用client,server架构,一个server可以对应多个client,client在使用的时候需要先连接到server,双方通过设置的一些callback来进行状态的同步。......
  • Qt显示图像之QGraphicsPixmapItem
    为防止不断地addItem导致内存增长,建议在初始化时newItem、scene->addItem。在合适的地方scene->removeItem(或scene->clear)或者item->setVisible。h头文件中#include<QGraphicsView>QGraphicsView*view;QGraphicsScene*scene;QGraphicsPixmapItem*m_pix=nullptr;cp......
  • COMP2045Goofspiel游戏的程序实现
    COMP2045课程2024介绍这门课相当于模块分数的40%。它要求你写C++程序和报告,以解决下面描述的任务。这个的最后期限演习时间为2024年4月29日星期一晚上23:55。在开始练习之前,请阅读整个文档。如果你对这个练习有任何问题,请在问答论坛上提问Moodle,在讲座后,在实验室,或在广告中的办......
  • QML之SpinBox
    今天使用SpinBox,为了使界面风格统一,需要修改控件的边框颜色,文本颜色,点击时的背景颜色和文本颜色,这时需要对SpinBox作一定程度的定制化。首先简单说明下SpinBox的参数:displayText:这是一个只读属性,表示SpinBox中显示的文本值。它等同于spinBox.textFromValue(spinBox.value,......
  • 【Azure Cloud Service】部署云服务时候遇见 Last exit code: 0. Last role exception
    问题描述部署云服务时候遇见Lastexitcode:0.Lastroleexception:(System.IO.FileNotFoundException)错误,提示无法加载System.Runtime。Recoveringrole...Applicationstartuptask0finishedsuccessfully.Lastexittime:[2024/03/27,20:23:31.142].Lastexit......