当先锋百科网

首页 1 2 3 4 5 6 7

在我们看很多代码的时候,都会看到有一个包名叫做spi,它是Java语言提供的一个比较重要的机制,而且在很多框架的底层实现中广泛用到,比如dubbo、hsf等微服务框架,比如commons-logging、jdbc等。

SPI的全名为Service Provider Interface,也就是服务提供者接口。为了更好的解耦,我们通常面向接口而不是面向实现类编程,spi机制就可以让我们面向接口编程,我们可以只根据接口就可以得到所有想要的实现类,那么这些实现类存储在哪里呢?存储在META-INF/services/目录下的以接口名的全限定名命名的文件中。

我们还是先来看一个demo吧,我们首先编写一个接口:

package com.mengzhidu.java.demo.spi;

public interface HiService {

    void hi();
}

然后我们编写三个实现类,分别如下:

package com.mengzhidu.java.demo.spi.impl;

import com.mengzhidu.java.demo.spi.HiService;

public class ChineseHiService implements HiService {

    public void hi() {
        System.out.println("用汉语打招呼");
    }
}
package com.mengzhidu.java.demo.spi.impl;

import com.mengzhidu.java.demo.spi.HiService;

public class EnglishHiService implements HiService {

    public void hi() {
        System.out.println("say hi in English");
    }
}
package com.mengzhidu.java.demo.spi.impl;

import com.mengzhidu.java.demo.spi.HiService;


public class UnknownHiService implements HiService {

    public void hi() {
        System.out.println("未知的问候...");
    }
}

然后我们在类路径下创建META-INF/services下面创建一个com.mengzhidu.java.demo.spi.HiService文件,然后写入如下代码:

com.mengzhidu.java.demo.spi.impl.ChineseHiService
com.mengzhidu.java.demo.spi.impl.EnglishHiService

这个配置文件中就注册了我们的两个服务类,然后我们可以用ServiceLoader.load()方法来加载所有实现这个接口的类,我们来看一下我们的主执行方法吧:

package com.mengzhidu.java.demo;

import com.mengzhidu.java.demo.spi.HiService;
import com.sun.tools.javac.util.ServiceLoader;

/**
 * 通过JDK来使用SPI机制的范例
 */
public class Demo {
    public static void main(String[] args) {
        ServiceLoader<HiService> services = ServiceLoader.load(HiService.class);
        for (HiService hiService : services) {
            hiService.hi();
        }
    }
}

这里我们来一个截图吧,如下所示:
这里写图片描述

需要说明的是,我们在配置文件中只列出了两个类,所以我们在用ServiceLoader.load方法来加载类的时候,也只能加载两个。

我们来看一下执行效果吧:
这里写图片描述

可以看到,我们这里的两个类就被我们注册进来了,并且被依次调用,但是我们并没有在Demo这个主类中进行任何的编码,这样就做到了代码和配置的解耦。

其实这个机制也比较简单,仔细看一下ServiceLoader的代码,它其实就是去特定的路径下去进行加载类,这里只需要注意在多线程环境下取当前线程的类加载器就可以了,并没有特别之处。

在微服务大行其道的今天,spi机制可以看做是一个单机版的微服务,每个接口都是一个服务,在META-INF/services 目录下的每个文件作为一个配置,注册了哪些会被用到的服务,然后我们就可以有一个类来自动的发现这些服务了。