一种SPI的可插拔接口的设计

背景与问题

​ 在追求效率和成本效益的背景下,软件复用成为了一种主流的做法,通过提供标准版本的产品或组件,提供了稳定、可靠且经过验证的功能。但对于那些有特定需求或环境的项目而言,标准版本可能无法完全满足他们的要求,这就引发了定制版本的需求。然而,过多的定制可能导致产品的标准化程度降低,从而增加了维护成本和复杂性。但是若一味遵循标准版本,可能无法满足特定项目的独特需求。因此,如何平衡标准版本与定制版本之间的关系,成为了一个关键的问题。

​ 要解决这个问题,关键在于提高产品的可配置性和可扩展性,提供一套清晰、开放的定制化接口开发体系。这样,在保持产品标准版本稳定性的同时,也能满足特定项目的定制化需求。实现软件复用与定制化之间平衡。

一个简单的案例

​ 以某项目短信模块为例,各个地区的短信服务商以及其提供的相关接口往往并不相同。这就带来了短信接口定制化的需求。

​ 在过去很长的一段时间内,我们只能通过源码交付的方式,让项目开发团队去自行修改相关的代码,然后重新打包部署。但这会带来几个问题:

  1. 需要付出一定的代价让项目开发团队理解“源码”。

  2. 修改后的代码会形成一个特定的地区分支,不便于代码仓库的集中管理。

  3. 当标准产品代码发生变动和升级,往往会要求地区分支进行反复的合并工作。

    这就导致软件复用与定制化之间发生了冲突,基于上述问题,我们引入了SPI机制。

解决问题的具体措施

我们的处理对策

​ 以短信中心为例,我们抽象了一个发送短信的接口,主要解决短信发给谁?以及发送内容的问题。

public interface SmsService<T> {
    /**
     * 发送短信
     * @param to 接收人
     * @param content 短信内容
     * @param params params
     * @return   String String
     */
    String sendMessage(String to, String content, T params);

    String getType();
}

​ 标准产品中所有涉及到短信部分的业务逻辑,全部通过该接口来进行操作,如:

@Resource
SmsService smsService;
......
......
String result = smsService.sendMessage(mobile, contentText, null);
......
接口的实现逻辑如何填充(自定义短信发送插件)

​ 首先,自定义一个maven工程。

 	<groupId>cn.xxx</groupId>
 	<artifactId>psf-sms-submail</artifactId>
    <version>2.0.0-SNAPSHOP</version>

​ 引入我们的短信接口定义包,如

		<dependency>
            <groupId>cn.xxx</groupId>
            <artifactId>mcs-api</artifactId>
            <version>${urbitech.essentia.version}</version>
        </dependency>

​ 实现已定义的接口

public class SubMailSmsServiceImpl implements SmsService {
	
	@Override
    public String sendMessage(String to, String content, Object params) {
        //自定义短信发送的逻辑,如调用厂商短信的api接口
    }
    
    @Override
    public String getType() {
        return "短信厂商的标识";
    }
    
}

​ 在resource目录下,新建 META-INF/spring.factories)文件,将相关实现类纳入到spring的容器管理。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.submail.service.impl.SubMailSmsServiceImpl
如何使用上述插件
  1. 常规做法(不推荐)

    修改主项目中pom.xml文件添加插件模块的依赖,然后重新编译打包。

  2. 通过启动项加载

    将相关自定义插件单独打包,并放置在项目部署指定目录下,通过-Dloader.path 参数加载,参考

​ -Dloader.path用于指定额外的类加载路径。能够在无代码侵入的前提下修改相关的项目依赖。这一做法在之前nacos自定义数据源的插件中也有提及,可参考nacos的启动脚本。此外这种方式也用于解决springboot fat jar的问题。
在这里插入图片描述
​ 当然有时候我们希望能够由产品提供默认实现,并在必要的情况下,加载自定义插件,此时我们可以手动实现一个类加载器,通过配置文件指定需要加载的Bean。如下图所示,其他使用方式与上文保持一致。

#业务驱动
service-driver:
  speech-service: xxx.service.impl.WySpeechServiceImpl 
@Configuration
public class ZwdmxConfig {

    /**
     * 指定驱动类名
     */
    @Value("${zwdmx.service-driver.speech-service}")
    private String speechServiceClassName;

    @Bean
    public SpeechService speechService() throws ClassNotFoundException {
        Class<?> speechServiceClass = Class.forName(speechServiceClassName);
        return (SpeechService) SpringUtil.getBean(speechServiceClass);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丨LucKy丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值