基于Java自实现的SPI机制的缺陷,以及使用的复杂性,在实际项目中可以通过Spring IOC来简易的实现一版。
核心做法是:
- 利用ApplicationContext的getBeansOfType()方法来获取目标接口的全部实现类
- 需要调用接口的地方, 对外预留属性对应值为接口实现的实际bean_name, 然后根据上面的结果是一个Map, key为接口实现的bean_name, value为接口实现类, 然后通过预留属性获取用户想要使用的具体实现类,属性可以预留默认值,即为系统默认;用户可再进行实现,然后通过预留的属性,切换为自己的bean_name,即可完成实现的动态切换
举个例子:
现在有一个日志持久化的动作, 然后定义一个接口,内部提供两个实现,一个是持久化到db, 一个是持久化到mongo, 类图如下
提供一个配置类属性,核心如下
@Data
@ConfigurationProperties(prefix = "customs.mq-message-properties")
public class MqMessageProperties {
/**
* 落库监听日志的实现类的beanName
* 默认使用Mongo落库,可切换为数据库
* @see com.ddf.boot.common.mq.persistence.LogMqPersistenceProcessor
*/
private String logMqPersistenceProcessorBeanName = "mongoLogMqPersistenceProcessor";
}
调用的地方可以使用如下方式
// 从Spring IOC中获取LogMqPersistenceProcessor接口的全部实现类
// SpringContextHolder这个是封装的静态类,其实就是实现了ApplicationContextAware接口,然后可以获取到ApplicationContext
Map<String, LogMqPersistenceProcessor> processorMap = SpringContextHolder.getBeansOfType(LogMqPersistenceProcessor.class);
// mqMessageProperties.getLogMqPersistenceProcessorBeanName即前面说到的预留的bean_name属性,通过这个属性来找到要使用的实现类
if (CollUtil.isNotEmpty(processorMap) && processorMap.containsKey(mqMessageProperties.getLogMqPersistenceProcessorBeanName())) {
processorMap.get(mqMessageProperties.getLogMqPersistenceProcessorBeanName()).persistence(poll, logMqListener);
} else {
log.warn("没有配置mq监听消费持久化方案");
}
如果用户想要切换持久化的目的地,只要修改上面的mqMessageProperties.getLogMqPersistenceProcessorBeanName
这个配置类这个字段的属性即可,用户如果不满意已有的两种实现,可以再重新实现接口LogMqPersistenceProcessor
, 持久化到Redis或者File随便,然后修改属性的bean_name
指向自己的实现即可。