前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SPI的简单应用

SPI的简单应用

作者头像
zhangheng
发布2020-04-28 18:12:19
5980
发布2020-04-28 18:12:19
举报

在查看java.sql.DriverManager源码时,发现有这么一个静态方法叫做loadInitialDrivers()。在这个方法里,我发现在项目启动中驱动管理器会从系统变量jdbc.drivers中获取具体的驱动实现并注册,其次会使用SPI注册驱动。这些我在 再谈驱动注册 中已经讲过了,那么什么是SPI?怎么用?

什么是java的SPI

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有不少框架用它来做服务的扩展发现,例如spring框架,dubbo和sofa分布式服务框架。简单来说,它就是一种动态发现的机制,举个例子来说,有个接口,想运行时动态的给它添加实现,你只需要添加一个实现即可。

具体是在JAR包的/META-INF/services/目录下建立一个文件,文件名是接口的全限定名,文件的内容可以有多行,每行都是该接口对应的具体实现类的全限定名。

运用场景

比如你想扩展一些框架,如spring的一些功能,就需要实现它接口,然后在运行时将你的jar包放到类路径下。

简单例子

假设我们有一个接口,方法是String hello(String message),我们允许不同的语言的hello返回不同的内容,例如中文返回的结果是你好 message,而英文返回HELLO message。那么这个SPI应用该怎么写呢?

我这有两个maven子项目,分别是learn-spi和learn-spi-cluster。learn-spi中写的是接口和main方法,learn-spi-cluster写的是实现类和资源文件。

注:打包时需要从父项目打包。

接口

首先我们定义一个接口

代码语言:javascript
复制
package com.gavinzh.learn.spi.interfaces;

/**
 * @author gavin
 */
public interface SPIService {
    public String hello(String message);
}

实现

我们再来定义两个实现类

代码语言:javascript
复制
package com.gavinzh.learn.spi.cluster.impl;

import com.gavinzh.learn.spi.interfaces.SPIService;

/**
 * @author gavin
 */
public class SPIServiceCN implements SPIService {
    public String hello(String message) {
        return "你好 " + message;
    }
}
代码语言:javascript
复制
package com.gavinzh.learn.spi.cluster.impl;

import com.gavinzh.learn.spi.interfaces.SPIService;

/**
 * @author gavin
 */
public class SPIServiceEN implements SPIService {
    public String hello(String message) {
        return "HELLO " + message;
    }
}

在实现的项目的resources目录下创建META-INF/services目录,并在该目录下创建一个文件,名为接口的全限定名com.gavinzh.learn.spi.interfaces.SPIService。内容为:

代码语言:javascript
复制
com.gavinzh.learn.spi.cluster.impl.SPIServiceCN
com.gavinzh.learn.spi.cluster.impl.SPIServiceEN

使用

一般情况下,使用SPI都是在接口所在的包中。所以我们在接口所在包中新建一个Main类。

代码语言:javascript
复制
package com.gavinzh.learn.spi;


import com.gavinzh.learn.spi.interfaces.SPIService;
import sun.misc.ClassLoaderUtil;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * @author gavin
 */
public class Main {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        SPIService spiService = null;
        ServiceLoader<SPIService> serviceLoader = ServiceLoader.load(SPIService.class, classLoader);
        Iterator<SPIService> iterator = serviceLoader.iterator();
        while (iterator.hasNext()) {
            spiService = iterator.next();
            System.out.println(spiService.hello("SPI"));
        }
    }
}

运行

我们将两个项目分别打包。在我的例子中,接口所在的包叫做learn-spi-1.0-SNAPSHOT.jar,实现类所在的包叫做learn-spi-cluster-1.0-SNAPSHOT.jar

接下来打开learn-spi-1.0-SNAPSHOT.jar,修改META-INF/MAINIFEST.MF,在文件末尾增加

代码语言:javascript
复制
Main-Class: com.gavinzh.learn.spi.Main
Class-Path: *.jar

第一行的作用是指定该jar包的运行入口,第二行的作用是在运行是jvm需要从应用类路径下加载的jar包,其实我们要加载的就是learn-spi-cluster-1.0-SNAPSHOT.jar

最后,将两个jar包放到同一个目录下,在该目录下执行

代码语言:javascript
复制
java -jar learn-spi-1.0-SNAPSHOT.jar

你就会看到输出以下内容

代码语言:javascript
复制
你好 SPI
HELLO SPI

总结

从jdk1.6开始,java支持了SPI,使用java.util.ServiceLoaderMETA-INF/services/寻找接口全限定名对应的文件,在文件中的每一行加载其实现类并实例化。

留一个问题,如何通过SPI技术扩展spring呢?以后有时间再写一篇关于SPI扩展spring的文章。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是java的SPI
  • 运用场景
  • 简单例子
    • 接口
      • 实现
        • 使用
          • 运行
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档