Spring ApplicationContext 使用getBeansOfType() 实现接口多个实现类的动态调用

使用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";
    }

}
相关推荐
崎岖Qiu25 分钟前
【Spring篇08】:理解自动装配,从spring.factories到.imports剖析
java·spring boot·后端·spring·面试·java-ee
述雾学java2 小时前
Spring Cloud 服务追踪实战:使用 Zipkin 构建分布式链路追踪
分布式·spring·spring cloud·zipkin
MonkeyKing_sunyuhua6 小时前
Ehcache、Caffeine、Spring Cache、Redis、J2Cache、Memcached 和 Guava Cache 的主要区别
redis·spring·memcached
考虑考虑7 小时前
@FilterRegistration和@ServletRegistration注解
spring boot·后端·spring
Hellyc9 天前
springcloud/springmvc协调作用传递验证信息
后端·spring·spring cloud
程序猿小D9 天前
[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+Vue实现的校园二手交易平台管理系统,推荐!
java·数据库·mysql·spring·vue·毕业设计·校园二手交易平台
转码的小石10 天前
Java面试复习指南:并发编程、JVM、Spring框架、数据结构与算法、Java 8新特性
java·jvm·数据结构·spring·面试·并发编程·java 8
转码的小石10 天前
Java面试复习:Java基础、面向对象编程、JVM原理、Spring框架解析
java·jvm·spring·面试·java基础·面向对象·复习
路修10 天前
Spring Bean生命周期(基于spring-beans-6.2.6分析)
java·spring