一,业务背景
在公司做一个Canal数据同步的时候,我们需要根据CanalEntry中传过来的表名,获取到相应的实例,然后调用相应的新增,更新删除数据的方法,
当前的问题是如何根据表名获取到相应的实例呢?
比较直观的是通过if…else来判断然后返回new出对应的实例。
代码
public IBaseDataOperateService getBeanByTableName(String tableName) {
if(tableName.equals("表A"){
return new CompanyProductServiceImpl();
}else if(tableName.equals("表B")){
return new RoleBaseDataOperateServiceImpl();
}else if{
.....
}
return null;
}
二,分析
如果操作的表不再改变这样,好像也不是不行,但是如果突然有其他表也需要做数据同步到BI库了,那么代码每次都需要改变,就会有大量的if…else…而这样的代码也不符合公司开发规范。
如果能在spring初始化好Bean之后提前把表名和实例的映射放到map中,然后用到的时候直接get,就避免了大量if…else…的使用。
三,ApplicationContextAware
spring会给实现此接口的类注入ApplicationContext对象,
通过源码跟踪了解到AbstractApplicationContext.class下的refresh()方法中的prepareBeanFactory这句跟Aware有关,我们还可以看到:ApplicationContextAware是在spring初始化完bean后才注入上下文的
prepareBeanFactory方法中涉及到上图红圈圈这个类,此类中的方法postProcessBeforeInitialization调用了此类中的invokeAwareInterfaces方法:
接下来进行代码改造,
@Slf4j
@Component
public class BaseDataSyncSelector implements ApplicationContextAware {
private Map<String, IBaseDataOperateService> baseDataOperateServiceMap;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IBaseDataOperateService> beansOfType = applicationContext.getBeansOfType(IBaseDataOperateService.class);
baseDataOperateServiceMap = new HashMap<>(beansOfType.size());
for (Map.Entry<String, IBaseDataOperateService> baseDataOperateServiceEntry : beansOfType.entrySet()) {
IBaseDataOperateService value = baseDataOperateServiceEntry.getValue();
baseDataOperateServiceMap.put(value.tableName(), value);
}
}
/**
* 根据表名获取相应的处理service
*
* @param tableName 表名
* @return
*/
public IBaseDataOperateService getBeanByTableName(String tableName) {
return baseDataOperateServiceMap.get(tableName);
}
}
然后再BaseServcie中进行调用,完美解决!
private void updateData(String tableName, List<CanalEntry.Column> beforeColumnsList,List<CanalEntry.Column> afterColumnsList) {
try {
//判断前后数据是否一致 如果一致则不处理
if(compareData(beforeColumnsList,afterColumnsList)){
return;
}
IBaseDataOperateService tableOperateService = baseDataSyncSelector.getBeanByTableName(tableName);
if (tableOperateService != null) {
tableOperateService.updateData(afterColumnsList);
}
}catch (Exception e){
log.warn("update data error,tableName:{},beforeColumnsList:{},afterColumnsList:{}", tableName,JSON_UTILS.toJson(beforeColumnsList), JSON_UTILS.toJson(beforeColumnsList));
}
}