背景:基于trade项目的启发,当项目中某个功能,需要根据业务场景ABC,区分不同的Service类处理。比如,针对一个统一的消息接受类,根据消息的不同场景,分散到不同的executor执行器里去执行;或者是针对一个统一的对外开放接口(Controller层),根据不同的业务场景,分配对应的service处理。
思考:针对这个场景,可以拿到容器里所有对应处理类(executor或者Service)。可以存成一个map<name,Class>的map格式,然后在对饮需要分配的地方,基于面向接口的思想,分配不同的处理类进行处理。
实现方式:
根据目前项目中的使用,有两个使用方式(以针对不同场景有多个executor为例)
1. 一个实现ApplicationContextAware的注册器。
实现ApplicationContextAware接口的类会实现一个setApplicationContext方法,可以获得当前ApplicationContext容器本身。
因此项目中的多个Executor实现类都设计为实现ExecutorInterface接口,为根据名称获取方便,可以定义一个getName的方法指定对应实现Executor类对应的名字。每个Executor实现类都@Component交由spring托管。 专门编写一个ExecutorRegistry类实现ApplicationContextAware接口,用于注册所有的executor实现类。
@Component
@Slf4j
public class MessageExecutorRegistry implements ApplicationContextAware {
private Map<String, MessageExecutor> messageExecutorMap; //用于存储executor实现类和类名对应的map,前置方法从这个map里拿取对应的executor实现类
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, MessageExecutor> messageExecutorBeanMap = applicationContext.getBeansOfType(MessageExecutor.class); //获取容器中所有messageExecutor实现类
messageExecutorMap = Maps.newHashMap();
messageExecutorBeanMap.forEach((beanName, executorBean) -> messageExecutorMap.put(executorBean.getBizCode(),executorBean));
log.info("存入map的bean数量:" + messageExecutorMap.size());
}
public MessageExecutor getExecutorByCode(String bizCode){
return messageExecutorMap.get(bizCode);
}
}
基于以上代码,可以获得一个包含容器内所有执行器类的map,并对外提供一个getByCode的方法,在需要的地方调用getByCode方法,获取所需的执行器。
2.利用ApplicationListener<ContextRefreshedEvent>,即spring的消息监听机制
实现一个实现了ApplicationListener的类,监听ContextRefreshedEvent时间,这个事件是在spring容器所有bean加载完成后,刷新整个容器用的。实现ApplicationListener的类需要实现onApplicationEvent方法,这个方法就是在event出发的时候调用,可以用event.getApplicationContext方法获得最新的容器,接下来的步骤和方法1是类似的,通过applicationContext.getBeansOfType获得所有bean,并将其封装到map里,对外提供根据name或code获取map中value也就是bean类本身的方法。
public class ServiceContainer implements ApplicationListener<ContextRefreshedEvent> {
private static Map<String, TrainBaseService> container = Maps.newHashMap();
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
Preconditions.checkNotNull(applicationContext, "applicationContext is null");
//获取TrainBaseService类型接口类
Map<String, TrainBaseService> serviceBeanMap = applicationContext.getBeansOfType(TrainBaseService.class);
if (MapUtils.isEmpty(serviceBeanMap)) {
return;
}
// 注册service bean到容器
registerAllService(serviceBeanMap);
}
}