Java SPI机制简介

简介: Java SPI机制简介            SPI是Service Provider Interfaces的简称。根据Java的SPI规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即Service Provider(服务提供者)。

Java SPI机制简介

 

         SPIService Provider Interfaces的简称。根据JavaSPI规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即Service Provider(服务提供者)。然后在使用的时候只要根据SPI的规范去获取对应的服务提供者的服务实现即可。为了便于理解,我们先来看一个使用SPI的示例。

         假设我们有一个日志服务LogService,其只定义了一个info方法用于输出日志信息,我们希望把它作为SPI,然后具体的实现由对应的服务提供者去实现。LogService的定义如下所示。

 

package com.elim.learn.basic.spi.service;

 

public interface LogService {

 

   public void info(String msg);

  

}

 

         然后基于这个服务我们会有自己的实现,示例中笔者用了三个实现,分别是ConsoleLogServiceFileLogServiceDBLogService,其实现都只是简单的打印一下日志类别信息,ConsoleLogService的实现如下所示,其它两个是类似的。

package com.elim.learn.basic.spi.service.impl;

 

import com.elim.learn.basic.spi.service.LogService;

 

public class ConsoleLogService implements LogService {

 

   @Override

   public void info(String msg) {

      System.out.println("----console log ----");

   }

 

}

 

         根据SPI的规范我们的服务实现类必须有一个无参构造方法。我们的SPI服务提供者需要将其在classpath下的META-INF/services目录下以服务接口全路径名命名的文件中写对应的实现类的全路径名称,每一行代表一个实现,如果需要注释信息可以使用“#”进行注释,根据官方的要求,这个文件的编码格式必须是UTF-8。我们示例中的LogService的全路径名是com.elim.learn.basic.spi.service.LogService,所以我们需要在类路径下的META-INF/services目录下创建一个名称为com.elim.learn.basic.spi.service.LogService文件。在本示例中我们一个提供了三个实现,所以该文件的内容如下。

#console

 

 

 

com.elim.learn.basic.spi.service.impl.ConsoleLogService

 

 

 

 

#file

 

 

 

 

com.elim.learn.basic.spi.service.impl.FileLogService

 

 

 

 

 

#db

 

 

 

 

 

 

 

com.elim.learn.basic.spi.service.impl.DBLogService

 

         至此,我们的服务定义和实现配置就完成了,接下来就是使用了。使用的时候核心是ServiceLoader类,我们需要通过这个工具类来加载服务提供者,即对应的服务实现,也需要通过它来获得对应的服务实现。ServiceLoader类的核心入口是其提供的三个可以创建ServiceLoader实例的静态方法,分别是load(Class<?> , ClassLoader)load(Class<?>)loadInstalled(Class<?>),三者的区别就在于使用的ClassLoader不一样。load(Class<?>)方法将使用当前线程持有的ClassLoaderloadInstalled(Class<?>)方法将使用最顶级的ClassLoader。三者的实现分别如下。

    public static <S> ServiceLoader<S> load(Class<S> service,

                                            ClassLoader loader)

    {

        return new ServiceLoader<>(service, loader);

    }

 

    public static <S> ServiceLoader<S> load(Class<S> service) {

        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        return ServiceLoader.load(service, cl);

    }

 

    public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {

        ClassLoader cl = ClassLoader.getSystemClassLoader();

        ClassLoader prev = null;

        while (cl != null) {

            prev = cl;

            cl = cl.getParent();

        }

        return ServiceLoader.load(service, prev);

    }

 

         ServiceLoader是实现了java.util.Iterator接口的,而且是基于我们所使用的服务的实现,所以可以通过ServiceLoader的实例来遍历其中的服务实现者,从而调用对应的服务提供者。示例如下。

   @Test

   public void test() {

      ServiceLoader<LogService> serviceLoader = ServiceLoader.load(LogService.class);

      LogService logService = null;

      for (Iterator<LogService> iter = serviceLoader.iterator(); iter.hasNext(); ) {

        logService = iter.next();

        logService.info("Hello SPI");

      }

      //由于ServiceLoader是实现了java.util.Iterator接口的,也可以使用增强的for循环

      for (LogService service : serviceLoader ) {

        service.info("Hello SPI");

      }

   }

 

         调用结果如下:


         在上述示例中我们的基于SPI规范的服务定义和服务实现都是在一个工程里面的,且都是可以看到源码的,但在实际应用中我们的服务提供者往往是以jar包的形式来提供对应的服务实现的。ServiceLoader不是一实例化以后立马就去读配置文件中的服务实现者,并且进行对应的实例化工作的,而是会等到需要通过其Iterator实现获取对应的服务提供者时才会加载对应的配置文件进行解析,具体来说是在调用IteratorhasNext方法时会去加载配置文件进行解析,在调用next方法时会将对应的服务提供者进行实例化并进行缓存。所有的配置文件只加载一次,服务提供者也只实例化一次,如需要重新加载配置文件可调用ServiceLoaderreload方法。有兴趣的朋友可以参考一下ServiceLoader的完整源码实现。

 

 

 

 

目录
相关文章
|
9天前
|
Java 数据库连接 开发者
Java的Shutdown Hook机制:优雅地关闭应用程序
Java的Shutdown Hook机制:优雅地关闭应用程序
17 1
|
11天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
1天前
|
安全 Oracle 小程序
01|Java简介与历史
01|Java简介与历史
7 0
|
2天前
|
Java 数据安全/隐私保护
java中异常处理机制
java中异常处理机制
8 1
|
2天前
|
算法 安全 Java
深入探索Java中的并发编程:CAS机制的原理与应用
总之,CAS机制是一种用于并发编程的原子操作,它通过比较内存中的值和预期值来实现多线程下的数据同步和互斥,从而提供了高效的并发控制。它在Java中被广泛应用于实现线程安全的数据结构和算法。
17 0
|
3天前
|
Java API 开发者
解密Java反射机制与动态代理
解密Java反射机制与动态代理
8 0
|
5天前
|
Java 数据库连接 开发者
Java中的异常处理机制详解
Java异常处理是确保程序健壮的关键,涉及Throwable的Error和Exception子类。Error由JVM抛出,不建议捕获;Exception分为检查异常(需要捕获)和未检查异常。处理异常的关键字有try、catch、finally、throw和throws。最佳实践包括捕获具体异常、不吞没异常、释放资源和避免滥用异常。示例展示了如何在main方法中处理IOException,并在finally块中进行资源清理。
|
9天前
|
缓存 NoSQL Java
17:缓存机制-Java Spring
17:缓存机制-Java Spring
22 5
|
9天前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
24 4
|
9天前
|
存储 前端开发 搜索推荐
13:Session机制实现用户登录与注销功能-Java Web
13:Session机制实现用户登录与注销功能-Java Web
24 3