JDK的 SPI 机制,Spring的 factories 机制


 

jdk的SPI机制

spi简介

模块之间一般推荐基于接口编程,不与具体的实现类耦合。抽象接口可能有多种不同的实现方案,eg. 日志可以使用不同的日志框架来实现,json|xml|excel可以使用不同的组件框架来解析,jdbc可以使用mysql、oracle等不同的数据库驱动实现,等等。

SPI全称Service Provider Interface,是jdk提供的一套服务发现机制,在开源框架、组件中十分常用。
 

spi常用场景

  • 组件替换机制,插拔式组件设计,eg. jdbc的数据库驱动、日志实现框架
  • 自定义扩展,eg. spring的一些接口可以通过spi来实现自定义扩展

双击shift搜索 META-INF/services,可以看到 jdk、jackson、junit、spring、日志组件、数据库驱动都使用到了jdk的spi机制。

 

spi使用约定

  • 在模块本身的resources下新建目录 META-INF/services
  • services目录下新建文件,以服务接口的全限定接口名作为文件名
  • 文件中指定该服务接口要使用的具体实现类
     

服务提供者即服务接口的实现者,服务接口模块只提供spi接口定义,服务提供者模块提供接口实现,并在 resources 下的 META-INF/services 中新建服务接口对应的配置文件,指定自身提供的接口实现类,来把自身提供的服务接口实现暴露出去。

spi实际是“ 基于接口编程+策略模式+配置文件 ”组合实现的动态加载机制,服务提供者通过本地配置文件的方式注册具体的服务接口实现类,服务接口模块通过对应的配置文件发现、获取到对应的接口实现类。

spi将服务接口与服务实现分离,解耦,将装配的控制权移到服务提供者本身,插拔式设计,提升了扩展性,在模块化设计中十分常用。

 

使用示例

1、使用者模块中定义的接口 AnimalServic

public interface AnimalService {

    void say();

}

 

2、提供者模块中提供的实现类 DogService、CatService

public class DogService implements AnimalService {

    @Override
    public void say() {
        System.out.println("汪汪汪");
    }

}
public class CatService implements AnimalService {

    @Override
    public void say() {
        System.out.println("喵喵喵");
    }

}

 

3、在提供者模块的 resources下新建目录 META-INF/services,services 目录下新建文件 com.chy.mall.service.AnimalService,文件名是接口的全限定名称,指定当前模块给该接口提供的实现

#全限定类名,通常只有一个类,也可以指定多个实现类,指定多个时一行一个
com.chy.demo.service.impl.DogService
com.chy.demo.service.impl.CatService

 

4、使用者模块中的使用示例

//此处只是创建 ServiceLoader 实例,尚未解析配置文件
ServiceLoader<AnimalService> serviceServiceLoader = ServiceLoader.load(AnimalService.class);

//遍历指定接口的实现类。临时变量需要声明为接口类型
for (AnimalService animalService : serviceServiceLoader) {
    //执行实现的方法
    animalService.say();
}

jdk的spi机制提供了 ServiceLoader 类用于加载、解析spi接口的配置文件,ServiceLoader实现了Iterable接口,可迭代,但只能以迭代器的方式进行操作。

使用 hasNext() 时才会加载解析 META-INF/services 下对应的接口配置文件,使用 next() 时才会通过实现类的 Class 对象的 newInstance() 方法(实质是通过反射调用无参构造器)创建实例。

 

spring的factories机制

factories机制简介

factories可以看做是spring结合自身需要提供的一种spi机制,设计思想和jdk的spi机制差不多。区别:jdk的spi机制,一个配置文件只能指定一个接口要使用的实现类;spring的factories机制,一个配置文件可以指定多个接口要使用的实现类。

factories机制在spring家族中广泛使用,双击shift搜索 spring.factories,可以看到spring、springboot、springcloud中都大量使用了factories机制。spring体系的很多扩展配置都是通过 spring.factories 指定的,比如应用初始化器 ApplicationContextInitializer、应用监听器 ApplicationListener。

注解本质是一种特殊接口,也可以用 factories 指定实现类,比如springboot中的 @EnableAutoConfiguration 注解就使用了factories指定要应用的实现类。

 

使用示例

1、使用者模块中定义的接口 AnimalService

public interface AnimalService {

    void say();

}

 

2、提供者模块中提供的实现类 DogService、CatService

public class DogService implements AnimalService {

    @Override
    public void say() {
        System.out.println("汪汪汪");
    }

}
public class CatService implements AnimalService {

    @Override
    public void say() {
        System.out.println("喵喵喵");
    }

}

 

3、在提供者模块的 resources下新建文件 META-INF/spring.factories,指定当前模块提供的接口实现

#spring.factories本质是一个properties文件,以键值对的形式写配置
#参数是接口,值是给该接口提供的实现类,要写全限定的
com.xxx.xxx.service.AnimalService=com.xxx.xxx.service.DogService


#可以指定多个实现类,有多个实现类时通常会 \ 换行写,以提高可阅读性
com.xxx.xxx.service.AnimalService=\
com.xxx.xxx.service.DogService,\
com.xxx.xxx.service.CatService


#IDEA中 \ 换行时会自动缩进,行首缩进也行
com.xxx.xxx.service.AnimalService=\
  com.xxx.xxx.service.DogService,\
  com.xxx.xxx.service.CatService

解析 spring.factories 时会自动剔除行首的空白字符、行尾的 \

如果 spring.factories 文件的图标不对,不是spring的绿色小叶子,多半是 META-INF、spring.factories 单词拼错了,或者连词线不是英文的。

 

4、使用者模块中的使用示例

//参数都是指定接口、要使用的类加载器,类加载器可以为null

//获取指定接口指定的各个实现类的类名列表
List<String> classNameList = SpringFactoriesLoader.loadFactoryNames(AnimalService.class, null);

//获取指定接口指定的各个实现类的实例列表,本质是通过反射调用实现类的无参构造器创建实例
List<AnimalService> instanceList = SpringFactoriesLoader.loadFactories(AnimalService.class, null);

 
如果spring提供的扩展配置,比如 ApplicationListener,已经提供了接口、解析处理,我们直接写实现类、在spring.factories中写配置即可。

 

总结

jdk的spi机制、spring的factories机制都是插拔式设计,常用于

接口可能要使用一个或多个实现类,但具体要使用的实现类尚不确定或者可能会切换、变更

以配置文件的方式指定要使用的具体实现类,根据配置文件动态加载要使用的实现类;面向接口编程,屏蔽了底层的具体实现,更改实现时无需更改上层代码。
 

接口、实现类通常是在不同模块中,也可以在同一个模块中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值