美文网首页程序员我爱编程Java 杂谈
Java SPI - ServiceLoader 使用简介

Java SPI - ServiceLoader 使用简介

作者: 老马啸西风2020 | 来源:发表于2018-05-27 11:36 被阅读249次

    问题引入

    以前一直想指定一套标准,让别人按照这个标准来实现,并编写好对应的容器。
    然后我在代码中动态获取这些实现,让代码运行起来。
    类似于写个 windows 环境,让开发者自己开发对应的软件。

    困难

    如何获取某个接口的实现?

    • 初步方案
      和同事讨论,是通过扫描包的 class 的方式。然后判断是否为定制标准的子类。

    • 缺点
      觉得很别扭,需要限定死实现类的包名称,而且性能也较差。

    SPI 的解决方式

    今天在阅读 hibernate-validator 源码时受到了启发。
    可以通过 SPI 的方式,更加自然的解决这个问题。

    SPI

    SPI 是 Service Provider Interfaces 的缩写。

    本文简单介绍下如何使用,具体原理,暂时不做深究。

    简单实现

    文件目录

    .
    ├── java
    │   └── com
    │       └── github
    │           └── houbb
    │               └── forname
    │                   ├── Say.java
    │                   ├── Sing.java
    │                   └── impl
    │                       ├── DefaultSay.java
    │                       └── DefaultSing.java
    └── resources
        └── META-INF
            └── services
                └── com.github.houbb.forname.Say
    

    定义接口和实现

    • Say.java
    /**
     * <p> 接口 </p>
     *
     * <pre> Created: 2018/5/27 上午10:36  </pre>
     * <pre> Project: tech-validation  </pre>
     *
     * @author houbinbin
     * @version 1.0
     * @since JDK 1.7
     */
    public interface Say {
    
        /**
         * 说
         */
        void say();
    
    }
    
    • DefaultSay.java
    package com.github.houbb.forname.impl;
    
    import com.github.houbb.forname.Say;
    
    /**
     * <p> </p>
     *
     * <pre> Created: 2018/5/27 上午10:37  </pre>
     * <pre> Project: tech-validation  </pre>
     *
     * @author houbinbin
     * @version 1.0
     * @since JDK 1.7
     */
    public class DefaultSay implements Say {
    
        @Override
        public void say() {
            System.out.println("Default say");
        }
    
    }
    

    编写 services 实现指定

    resources 目录下,创建 META-INF/services 文件夹,以接口全路径名
    com.github.houbb.forname.Say 为文件名称,内容为对应的实现类全路径。
    如果是多个,就直接换行隔开。

    • com.github.houbb.forname.Say
    com.github.houbb.forname.impl.DefaultSay
    

    测试

    • SayTest.java
    public class SayTest {
    
        @Test
        public void spiTest() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            ServiceLoader<Say> loader = ServiceLoader.load(Say.class, classLoader);
    
            for (Say say : loader) {
                say.say();
            }
        }
    
    }
    
    • 测试结果
    Default say
    

    简单总结

    Java 中,可以通过 ServiceLoader 类比较方便的找到该类的所有子类实现。
    META-INF/services 下的实现指定和实现子类实现完全可以和接口定义完全分开。

    • 麻烦的地方

    每次都要手动创建实现指定文件,比较繁琐。

    Auto 就为解决这个问题而生。

    Auto 版本

    jar 的引入

        <dependencies>
            <dependency>
                <groupId>com.google.auto.service</groupId>
                <artifactId>auto-service</artifactId>
                <version>1.0-rc4</version>
                <optional>true</optional>
            </dependency>
        </dependencies>
    

    接口和定义

    • Sing.java
    /**
     * <p> 接口 </p>
     *
     * <pre> Created: 2018/5/27 上午10:36  </pre>
     * <pre> Project: tech-validation  </pre>
     *
     * @author houbinbin
     * @version 1.0
     * @since JDK 1.7
     */
    public interface Sing {
    
        /**
         * 唱歌
         */
        void sing();
    
    }
    
    • DefaultSing.java
    @AutoService(Sing.class)
    public class DefaultSing implements Sing {
    
        @Override
        public void sing() {
            System.out.println("Sing a song...");
        }
    
    }
    

    测试

    • SingTest.java
    public class SingTest {
    
        @Test
        public void spiTest() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            ServiceLoader<Sing> loader = ServiceLoader.load(Sing.class, classLoader);
    
            for (Sing sing : loader) {
                sing.sing();
            }
        }
    
    }
    
    • 结果
    Sing a song...
    

    简单总结

    通过 google 的 auto,可以在编译时自动为我们生成对应的接口实现指定文件。在 target 对应的文件下可以看到。

    实现原理,也相对简单。通过 java 的编译时注解,生成对应的文件即可。

    项目源码

    github spi

    相关文章

      网友评论

        本文标题:Java SPI - ServiceLoader 使用简介

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