使用ApplicationContext的getBeansOfType实现接口多实现类的动态调用。
冷知识
org.springframework.beans及org.springframework.context这两个包是Spring IoC容器的基础,其中重要的类有BeanFactory,BeanFactory是IoC容器的核心接口,其职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖关系。
ApplicationContext作为BeanFactory的子类,在Bean管理的功能上得到了很大的增强,也更易于与Spring AOP集成使用。
业务场景
当一个接口在不同的业务场景下会使用不同的实现类。从使用方式上类似SPI的用法,但是不方便,可以借助ApplicationContext的getBeansOfType来实现接口多个实现类的动态调用。
通过 ApplicationContext.getBeansOfType(class) 获取某一接口的所有实现类,并通过枚举完成策略模式,替代 if/else,使代码更加优雅易于拓展。
该方法返回一个接口的全部实现类(前提是所有实现类都必须由Spring IoC容器管理)
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
插件注册 SendDataRegister
Map 中的 value 是各个 key 对应的策略实现类。
一个策略接口被多个策略实现类所实现,具体使用哪一种根据用户选择的类型来和 Map 里的 key 做匹配,获取对应的实现来调用具体的策略方法。
使用 ConcurrentHashMap ,而不使用 HashMap,是 put 的时候,键和值都不能为空,防止 key 对应的实现类没有注入进去,导致空指针的问题。
csharp
@Service("sendDataRegister")
public class SendDataRegister implements InitializingBean, ApplicationContextAware {
HashMap<String, SendDataPlugin> sendDataPluginMap = new HashMap<>();
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("init applicationContext ok! ");
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, SendDataPlugin> beanMap = applicationContext.getBeansOfType(SendDataPlugin.class);
log.info("数据推送配置注册: " + JSONObject.toJSONString(beanMap));
for(String name : beanMap.keySet()){
log.info("注册名称: " + name);
sendDataPluginMap.put(name, beanMap.get(name));
}
}
public SendDataPlugin getSendDataPlugin(String name){
return sendDataPluginMap.get(name);
}
public Set<String> getSendDataPluginNames(){
return sendDataPluginMap.keySet();
}
}
插件接口 SendDataPlugin
定义一个插件接口,所有插件都需要实现这个接口。
csharp
public interface SendDataPlugin {
//向智慧城市平台,推送车场数据(入场/离场等)
public boolean pushDataToChannel(SmartParkConfig parkConfig/*车场配置*/, JSONObject mqData/*mq车场消息*/);
}
接口实现类
csharp
@Service("beijingDataPushPluginService")
public class BeijingDataPushPluginService implements SendDataPlugin {
@Override
public boolean pushDataToChannel(parkConfig, mqData) {
//向【北京丰台城市平台】推送车场数据(车辆入场/离场)
if(mqData.getString("pushEvent").equals("park:event:in")){
return this.pushCarInData(parkConfig, mqData);
}
if(mqData.getString("pushEvent").equals("park:event:in")){
return this.pushCarOutData(parkConfig, mqData);
}
return false;
}
}
//Qingdao、Hangzhou...
@Service("shangHaiDataPushPluginService")
public class ShangHaiDataPushPluginService implements SendDataPlugin {
@Override
public boolean pushDataToChannel(parkConfig, mqData) {
//向【上海城市平台】推送车场数据(车辆入场/离场/心跳)
if(mqData.getString("pushEvent").equals("park:event:in")){
return this.pushCarInData(parkConfig, mqData);
}
if(mqData.getString("pushEvent").equals("park:event:in")){
return this.pushCarOutData(parkConfig, mqData);
}
//pushHearbeatData() //推送心跳
//repeatPushdDataTrans() 数据重传
return true;
}
}
测试
csharp
@RestController
@RequestMapping("/cityPlatform")
public class SpiTestController {
@Resource
private SendDataRegister sendDataRegister;
@GetMapping(value = "/initSendCity")
public String initSendCity(String cityId) {
// ParkConfig parkConfig = cityPlatformParkingConfigService.selectBy(cityId);
// String serviceName = parkConfig.getPlatformPluginName();
String serviceName1 = "beijingDataPushPluginService";
SendDataPlugin beijingDataPushPluginService = sendDataRegister.getSendDataPlugin(serviceName1);
beijingDataPushPluginService.pushDataToChannel(parkDbConfig, mqData);
String serviceName2 = "shangHaiDataPushPluginService";
SendDataPlugin shangHaiDataPushPluginService = sendDataRegister.getSendDataPlugin(serviceName2);
shangHaiDataPushPluginService.pushDataToChannel(parkDbConfig, mqData);
return "push success";
}
}