美文网首页源码程序员@IT·互联网
SPI(Service Provider Interface)机

SPI(Service Provider Interface)机

作者: holly_wang_王小飞 | 来源:发表于2016-09-01 15:51 被阅读691次

    上一篇文章中看到commons-logging,LogFactory 在查找具体实现类的时候 有一种是从
    META-INF/services/org.apache.commons.logging.LogFactory 的第一行读取具体实现类。其实这个是java提供的一种扩展机制<b>SPI</b>,比如日志框架 会有很多其他实现,还有最著名用到<b>SPI</b>机制的就是<b>java.sql.Driver的SPI</b>,mysql,oracle 等驱动包都是不同的,<b>java可以结合不同的数据库驱动操作数据库</b>。这样的扩展机制感觉就是一种开源精神和扩展机制,还有spring中好多接口都是提供了一个默认的(default)的实现 在加载不到其他的实现的时候才走default实现,这就是留下的扩展,无论对于项目本身开发人员还是第三方的都是一大福音。

    oracle的doc地址:SPI-intro

    官方提供的是例子是一个<em>Java Sound API</em>,提供了一些寡欲读写混淆处理转换的接口 然后实现<em>Java Sound API</em>的提供基本的服务,但是这无法满足一些第三方开发者自己的实现,为了解决这个问题让这些第三方的实现能够像插件一样的嵌入到系统中提供服务就出现了<b>SPI</b>。
      除了阅读官方的demo自己实现一个简单的demo

    spitestproject.png
    • SPIService接口
        定义规范spiTest方法
    package spi.test;
    
    /**
     * @author holly.wang
     */
    public interface SPIService {
    
        public void spiTest();
    }
    
    • SPIServiceImpl class
        SPIService的一个实现
    package spi.test;
    
    /**
     * @author holly.wang
     */
    public class SPIServiceImpl implements SPIService {
    
        @Override
        public void spiTest() {
            System.out.println("holly.wang spi test");
            System.out.println("简书博客首页:http://www.jianshu.com/users/0d5a83b32e8f/latest_articles");
        }
    }
    
    • SpiTest类
        测试方法
    package spi.test;
    import java.util.Iterator;
    import sun.misc.Service;
    
    /**
     * @author holly.wang
     */
    public class SpiTest {
    
        /**
         * @param args
         * @throws IllegalAccessException
         * @throws InstantiationException
         * @throws ClassNotFoundException
         */
        public static void main(String[] args) throws InstantiationException, IllegalAccessException,
                                              ClassNotFoundException {
            Iterator it = Service.providers(SPIService.class);
            while (it.hasNext()) {
                SPIService service = (SPIService) it.next();
                service.spiTest();
            }
        }
    }
    

    下面我们看path中的/META-INF/services/spi.SPIService文件

    spi.test.SPIServiceImpl
    

    第一行就是指定要用的是哪个实现SPIService的类 只要有其他实现 就可以指定 这里我们指定为自己的实现spi.test.SPIServiceImpl 运行SpiTest的main方法

    SpiTest-runbeafore.png

    跑完后:


    SpiTest-runafter.png

    看到已经正确的输出。测试完成。

    关于SPI的规范和执行流程

    • 在META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为Api具体实现类的全限定名
    • 使用ServiceLoader类动态加载META-INF中的实现类
    • 如SPI的实现类为Jar则需要放在主程序classPath中
    • Api具体实现类必须有一个不带参数的构造方法

    这个demo很简单,感觉完全没必要这样麻烦的使用,但是SPI的机制是支持第三方的实现,你自己定义一组规范,任何人都可以有自己的实现,假如别人想用到别人的实现 不想用你默认的实现 那他的作用就来了 像插入一个插件一样easy。

    相关文章

      网友评论

        本文标题:SPI(Service Provider Interface)机

        本文链接:https://www.haomeiwen.com/subject/xancettx.html