Java的SPI机制ServiceLoader

117 阅读2分钟

前言

SPI相信大家都听过,来看一下SPI的定义:

SPI(Service Provider Interface) 是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。

我们平时开发接触的比较频繁的SPI机制就是java的ServiceLoader(META-INF/services)与SpringBoot的SpringFactoriesLoader(META-INF/spring.factories),这次我们来介绍一下java的ServiceLoader的使用,并且用它来实现一个策略+工厂模式。

开始

创建接口

/**
 * @author wyh
 * @date 2021/6/3 14:32
 */
public interface MobilePhone {

    String getName();

    BrandEnum getBrand();
}
/**
 * @author wyh
 * @date 2021/6/3 16:29
 */
public enum BrandEnum {
    APPLE,MI,HUAWEI
}

创建工厂类

/**
 * 使用serviceLoader动态获取某个interface或abstract class的实现
 * <p>
 * 如不使用google的autoService{@link com.google.auto.service.AutoService},
 * 则需要自己创建{@code META-INF/services/${your.service.path}}文件,
 * 内容为${your.service.path}所有实现类的全限定名,
 * 如果有多个实现需要换行<p>
 *
 * @author wyh
 * @date 2021/6/3 16:31
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PhoneFactory {

    public static MobilePhone getByName(BrandEnum brand) {
        if (brand != null) {
            ServiceLoader<MobilePhone> serviceLoader = ServiceLoader.load(MobilePhone.class);
            for (MobilePhone mobilePhone : serviceLoader) {
                if (brand.equals(mobilePhone.getBrand())) {
                    return mobilePhone;
                }
            }
        }
        throw new UnknownTypeException();
    }
}

使用

测试包下或客户端中创建各种实现类

/**
 * @author wyh
 * @date 2021/6/3 14:40
 */
@AutoService(MobilePhone.class)
public class IPhone implements MobilePhone {

    @Override
    public String getName() {
        return "IPHONE7";
    }

    @Override
    public BrandEnum getBrand() {
        return BrandEnum.APPLE;
    }
}
/**
 * @author wyh
 * @date 2021/6/3 14:41
 */
@AutoService(MobilePhone.class)
public class XiaoMi implements MobilePhone {

    @Override
    public String getName() {
        return "MI9";
    }

    @Override
    public BrandEnum getBrand() {
        return BrandEnum.MI;
    }
}
/**
 * @author wyh
 * @date 2021/6/3 16:38
 */
@AutoService(MobilePhone.class)
public class Huawei implements MobilePhone {

    @Override
    public String getName() {
        return "MATE50";
    }

    @Override
    public BrandEnum getBrand() {
        return BrandEnum.HUAWEI;
    }
}

这里为了方便使用了google提供的一个工具包提供的@AutoService注解,这样我们就无需去创建META-INF/services下的文件了(手动创建的规则详见工厂类的注释),编译期会自动帮我们生成

        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>${annotation-processor.version}</version>
        </dependency>

获取实现

/**
 * @author wyh
 * @date 2021/6/3 14:45
 */
class ServiceLoaderTest {

    @Test
    void load() {
        Assertions.assertNotNull(PhoneFactory.getByName(BrandEnum.APPLE));
        Assertions.assertNotNull(PhoneFactory.getByName(BrandEnum.HUAWEI));
        Assertions.assertNotNull(PhoneFactory.getByName(BrandEnum.MI));
    }
}

打完收工:p