前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA SPI 是怎么实现的?

JAVA SPI 是怎么实现的?

作者头像
王小明_HIT
发布2020-06-04 16:58:22
7040
发布2020-06-04 16:58:22
举报
文章被收录于专栏:程序员奇点程序员奇点

JAVA SPI 是怎么实现的?

SPI 是什么?

SPI(Service Provider Interface) ,是 JDK 内置的一种提供发现机制。SPI 是一种动态替换发现的机制。

SPI类图

JAVA SPI 实现

  1. 定义一组接口,接口有多种实现
代码语言:javascript
复制
public interface IShout {
    void shout();
}
public class Cat implements IShout {
    @Override
    public void shout() {
        System.out.println("miao miao");
    }
}
public class Dog implements IShout {
    @Override
    public void shout() {
        System.out.println("wang wang");
    }
}
  1. 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 (org.foo.demo.IShout文件),内容是要应用的实现类(这里是org.foo.demo.animal.Dog和org.foo.demo.animal.Cat,每行一个类)。

文件位置

代码语言:javascript
复制
- src
    -main
        -resources
            - META-INF
                - services
                    - org.foo.demo.IShout

文件内容

代码语言:javascript
复制
org.foo.demo.animal.Dog
org.foo.demo.animal.Cat
  1. ServiceLoaader 加载配置文件中指定的实现
代码语言:javascript
复制
public class SPIMain {
    public static void main(String[] args) {
        ServiceLoader<IShout> shouts = ServiceLoader.load(IShout.class);
        for (IShout s : shouts) {
            s.shout();
        }
    }
}

输出:

代码语言:javascript
复制
wang wang
miao miao

Java SPI 实现原理

  1. 应用程序调用 ServiceLoader.load 方法创建一个 ServiceLoader,并实例化类中的成员变量。
  • load(Classloader 类型,类加载器)
  • acc(AccessControlContext 类型,访问控制器)
  • providers(LinkedHashMap<String,S>)类型,用于缓存加载成功的类
  • lookupIterators (实现迭代器功能)
代码语言:javascript
复制
 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);
    }
  1. 应用程序通过迭代器接口 读取META-INF/services/下的配置文件,然后实例化:

1).读取配置文件

代码语言:javascript
复制
 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);
                }
            }

2). 通过反射方法Class.forName()加载类对象,并用instance()方法将类实例化。

3). 把实例化后的类缓存到providers对象中

JAVA SPI 应用场景

数据库DriverManager、Spring、ConfigurableBeanFactory等都用到了SPI机制。

在JDBC4.0之前,连接数据库的时候,通常会用Class.forName("com.mysql.jdbc.Driver")这句先加载数据库相关的驱动,然后再进行获取连接等的操作。而JDBC4.0之后不需要Class.forName来加载驱动,直接获取连接即可,这里使用了Java的SPI扩展机制来实现。

  1. 在mysql-connector-java-5.1.45.jar中,META-INF/services目录下会有一个名字为java.sql.Driver的文件:
代码语言:javascript
复制
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
  1. Mysql DriverManager实现

DriverManager中有一个静态代码块如下:

代码语言:javascript
复制
static {
	loadInitialDrivers();
	println("JDBC DriverManager initialized");
}

loadInitialDrivers方法,loadInitialDrivers用法用到了上文提到的spi工具类ServiceLoader:

代码语言:javascript
复制
 public void run() {

        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> driversIterator = loadedDrivers.iterator();

...
}

总结

Java的SPI机制就是为某个接口寻找到相关的服务实现

程序员开发者社区
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员奇点 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JAVA SPI 是怎么实现的?
    • SPI 是什么?
      • JAVA SPI 实现
        • Java SPI 实现原理
          • JAVA SPI 应用场景
            • 总结
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档