前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java SPI机制浅析

Java SPI机制浅析

作者头像
孟君
发布2019-09-17 14:56:03
4560
发布2019-09-17 14:56:03
举报

在平时开发项目的过程中,相信很多读者都看到过这样的目录,/META-INF/services目录,该目录下的文件名是接口的全称,其内容是具体的接口实现。这就是使用了SPI机制。如:

  • mysql-connector
  • JDK中的nio SPI

再如,logback-classic

接下来,我们就来聊聊java SPI机制,文章内容主要包含如下几个部分:

  • SPI概念和规范
  • 根据规范编写一个简单的SPI
  • 小结

一、SPI概念和规范

1.1 SPI概念

SPI全称为Service Provicder Interface,是JDK内置的一种服务提供发现功能,一种动态替换发现的机制。

举个例子,要想在运行时动态给一个接口添加实现,只需要添加一个实现即可。比如JDBC的数据库驱动模块,不同数据库连接驱动接口相同但实现类不同,通常各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。

1.2 SPI规范

使用SPI也需要遵循一定的规范,主要包含如下几点:

  • 需要设置/META-INF/目录
    • /META-INF/services
    • 放到classpath下
  • /META-INF/services/目录下放置配置文件
    • 文件名是接口全路径名
    • 文件内部是要实现的接口实现类
    • 文件编码为UTF-8
  • 使用ServiceLoad的load方法

二、SPI示例

  • 编写一个GreetingService接口
代码语言:javascript
复制
package com.wangmengjun.tutorial.spi;

public interface GreetingService {

  void sayHello();
}
  • 编写2个实现类,分别输出英文和中文
代码语言:javascript
复制
package com.wangmengjun.tutorial.spi.impl;

import com.wangmengjun.tutorial.spi.GreetingService;

public class EnglishGreetingServiceImpl implements GreetingService{

  public void sayHello() {
    System.out.println("Hello , This is SPI");
    
  }

}
代码语言:javascript
复制
package com.wangmengjun.tutorial.spi.impl;

import com.wangmengjun.tutorial.spi.GreetingService;

public class ChineseGreetingServiceImpl implements GreetingService {

  public void sayHello() {
    System.out.println("你好,这是SPI");
    
  }

}
  • 创建META-INF文件目录并设置实现类
  • 使用ServiceLoader
代码语言:javascript
复制
package com.wangmengjun.tutorial.spi;

import java.util.Iterator;
import java.util.ServiceLoader;

public class SpiMain {

  public static void main(String[] args) {
  
      ServiceLoader<GreetingService> loader= ServiceLoader.load(GreetingService.class);
      Iterator<GreetingService> greetingIter = loader.iterator();
      while(greetingIter.hasNext()) {
          GreetingService service=  greetingIter.next();
          System.out.println(service.getClass().getName());
          service.sayHello();
      }
  }
}

输出:

代码语言:javascript
复制
com.wangmengjun.tutorial.spi.impl.EnglishGreetingServiceImpl
Hello , This is SPI
com.wangmengjun.tutorial.spi.impl.ChineseGreetingServiceImpl
你好,这是SPI

经过上述几个步骤,一个spi的简单示例就完成了。

当执行ServiceLoader.load(GreetingService.class)构造完ServiceLoader实例我们可以看到这个时lookupIterator1的值还是null的。这个时候还没有去读取配置文件中的实现类信息。

代码语言:javascript
复制
    // The lazy-lookup iterator for iterator operations
    private Iterator<Provider<S>> lookupIterator1;
    private final List<S> instantiatedProviders = new ArrayList<>();

当使用迭代器去遍历的时候,才会读取对应的配置文件去解析,调用hasNext方法的时候会去加载配置文件进行解析。文件读取采用BufferedReader的readLine来读取并解析。

三、小结

从上述的示例可以看出:虽然ServiceLoader也算是使用的延迟加载,但是需要通过迭代器迭代获取,所有配置的实现类都要实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

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

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

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