当前位置: 首页 > news >正文

Data Processer

DP项目创建之初作为中间层处理WMS系统的报表数据,正常WMS数据是SQL直接返回结果到WMS展示,如果需要一些特殊处理,比如多个数据库的数据整合,复杂数据计算等需求,则使用DP中转处理。
​ 后续添加了BPM对接CBS资金系统的功能,同样是作为中转,比如BPM流程到某个节点,调用DP的接口,由DP向CBS发起付款申请,并记录日志和管理申请结果,根据申请结果和付款结果再调用BPM的接口审批流程。中转的原因是BPM项目限制过多,且架构太老、部署复杂,开发不方便,所以放到DP来完成主要的业务操作。

一、自定义注解实现数据源切换

自定义注解

@Target(ElementType.METHOD)// 用于方法上
@Retention(RetentionPolicy.RUNTIME)//运行时保留
@Documented
public @interface DataSource {/*** 目标 datasource* @return datasource*/DataSourceEnum value();
}@Getter
public enum DataSourceEnum {WMS("wms",1),MES("mes",2),SAP("sap",7),BPM("bpm",8),UNKNOWN("unknown",-1),;private final String key;private final Integer value;DataSourceEnum(String key, Integer value) {this.key = key;this.value = value;}public static DataSourceEnum getByValue(Integer value){for (DataSourceEnum mapping : DataSourceEnum.values()) {if (Objects.equals(mapping.getValue(), value)){return mapping;}}return UNKNOWN;}
}

切面类

ProceedingJoinPoint 类
ProceedingJoinPoint 是 AspectJ 框架提供的接口,它是环绕通知(@Around)中使用的特殊类型的连接点。其他通知使用JoinPoint即可。
作用:

  1. 代表被拦截的方法执行点
  2. 在环绕通知中,允许控制是否执行目标方法
  3. 提供对目标方法的完全控制,包括修改参数、处理返回值、捕获异常等
    主要方法:
  4. proceed() - 执行目标方法
  5. proceed(Object[] args) - 使用指定参数执行目标方法
  6. getSignature() - 获取方法签名
  7. getTarget() - 获取目标对象
  8. getArgs() - 获取方法参数

joinPoint.proceed() 方法调用
这行代码是环绕通知中最关键的部分。
作用:
实际执行被拦截的目标方法
如果不调用 proceed(),目标方法将不会被执行
返回目标方法的执行结果


@Slf4j
@Aspect
@Component
public class DataSourceAspect {@Around("@annotation(com.lzcer.dataprocessor.frame.annotation.DataSource)")//环绕通知,拦截所有添加了@DataSource注解的方法public Object changeDataSource(ProceedingJoinPoint joinPoint) throws Throwable {//ProceedingJoinPoint 用于获取对目标方法的完全控制MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获得目标方法签名Method method = signature.getMethod();//得到目标方法DataSource annotation = method.getAnnotation(DataSource.class);//获取实例注解,用于解析用到的数据源DataSourceEnum datasource = annotation.value();log.info("========change datasource : {}========", datasource.getKey());DynamicDataSourceContextHolder.setDataSource(datasource.getKey());StopWatch stopWatch = new StopWatch();stopWatch.start();Object result = joinPoint.proceed();stopWatch.stop();log.info("method: {} time cost :{} ms",method.getName(),stopWatch.getTotalTimeMillis());return result;}
}

设置当前数据源,使用ThreadLocal存储当前线程使用的数据源

public class DynamicDataSourceContextHolder {// 使用ThreadLocal存储每个线程的数据源标识private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();// 设置当前线程的数据源标识public static void setDataSource(String dataSourceKey) {CONTEXT_HOLDER.set(dataSourceKey);}// 获取当前线程的数据源标识public static String getDataSource() {return CONTEXT_HOLDER.get();}// 清除当前线程的数据源标识public static void clearDataSource() {CONTEXT_HOLDER.remove();}
}

切换数据源的核心实现
Spring 提供的 AbstractRoutingDataSource 是实现动态数据源切换的核心类
它的核心机制是通过 determineCurrentLookupKey() 方法获取当前应该使用的数据源标识,然后在已配置的数据源映射中查找对应的实际数据源

public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(Object defaultDataSource, Map<Object, Object> targetDataSources){super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSources);}@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSource();}
}

数据源配置

@Data
@Component
@ConfigurationProperties("datasource")//通过@ConfigurationProperties注解将配置文件中的 datasource 部分映射为 Java 对象
public class DataSourceConfig {private Map<String, DbConfig> dbs;@Datapublic static class DbConfig {private String url;private String username;private String password;private String driverClassName;}
}
@AllArgsConstructor
@Configuration
public class DataSourceManager {private static final Map<Object, Object> DATA_SOURCE_MAP = new HashMap<>();private final DataSourceConfig config;@Bean(name = "dynamicDataSource")@Primary//优先选择public DynamicDataSource createDynamicDataSource() {// 遍历配置文件中的所有数据源配置config.getDbs().forEach((k, v) -> {if (DataSourceEnum.SAP.getKey().equals(k)) {// SAP 数据源使用 HikariDataSourceHikariDataSource dataSource = new HikariDataSource();dataSource.setDriverClassName(v.getDriverClassName());dataSource.setJdbcUrl(v.getUrl());dataSource.setUsername(v.getUsername());dataSource.setPassword(v.getPassword());DATA_SOURCE_MAP.put(k, dataSource);} else {// 其他数据源使用 DruidDataSourceDruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(v.getDriverClassName());dataSource.setUrl(v.getUrl());dataSource.setUsername(v.getUsername());dataSource.setPassword(v.getPassword());List<Filter> filters = new ArrayList<>();filters.add(statFilter());filters.add(wallFilter());dataSource.setProxyFilters(filters);try {dataSource.setFilters("stat,wall,slf4j");} catch (SQLException e) {e.printStackTrace();}DATA_SOURCE_MAP.put(k, dataSource);}});// 创建 DynamicDataSource,传入默认数据源和所有数据源映射return new DynamicDataSource(DATA_SOURCE_MAP.get("wms"), DATA_SOURCE_MAP);}
}

总结
实现数据源切换主要是用到了Spring框架提供的AbstractRoutingDataSource类,需要继承这个类,重写里面的determineCurrentLookupKey()方法。

  1. 需要在配置文件中配置号多个数据源的url、username、password、DriverClass等
  2. 新建DataSourceConfig类通过@ConfiguationProperties注解将配置文件中的配置变成JAVA中的对象
  3. 在 DataSourceManager 中创建实际的数据源对象
  4. DynamicDataSource初始化,DynamicDataSource 继承 AbstractRoutingDataSource,在初始化时设置默认数据源和目标数据源映射
  5. 运行时切换数据源,DataSourceAspect 切面通过 @Around(“@annotation(com.lzcer.dataprocessor.frame.annotation.DataSource)”) 拦截被 @DataSource 注解标记的方法
  6. DynamicDataSourceContextHolder.setDataSource() 将数据源键值存储到 ThreadLocal 中
  7. 执行目标方法触发数据源选择,当方法中有数据库操作时触发,调用我们实现的 determineCurrentLookupKey(),获取实际的数据库源,返回数据库源
  8. 执行数据库操作
http://www.dtcms.com/a/308684.html

相关文章:

  • QQ输入法设置快捷键,更快造词或自定义短语
  • 智慧零售商品识别准确率↑32%:陌讯多模态融合算法实战解析
  • 2025年IntelliJ IDEA最新下载、安装教程,附详细图文
  • 人工智能如何助力工业领域实现可持续发展?
  • 循环神经网络RNN原理精讲,详细举例!
  • 人工智能与新闻传播:信息传播的变革
  • vue-seamless-scroll 与 echarts 三联水球图循环滚动的渲染难题-出现短暂空白
  • el-button长按触发事件(含未响应的解决方案)
  • 【AI大模型】披着羊皮的狼--自动化生成越狱提示的系统(ReNeLLM)
  • QtConcurrent::run函数
  • React 项目环境如何安装使用?
  • es的histogram直方图聚合和terms分组聚合
  • pig Cloud中分布式锁的使用(setIfAbsent)
  • 扫描发票自动录入财务系统怎么做?
  • 定点数 与 浮点数
  • 软件项目中如何编写项目计划书?指南
  • ros2 tf2详解
  • 基于 C 语言视角:流程图中分支与循环结构的深度解析
  • 敏捷开发轻量级看板工具:提升效率的秘密武器
  • 【02】大恒相机SDK C#开发 —— 初始化相机,采集第一帧图像
  • 基于单片机智能油烟机设计/厨房排烟系统设计
  • mac fusion win11虚拟机 不能正确识别bitlocker USB
  • wordpress配置文章详情页自动生成目录点击定位
  • 计算机存储正数,负数
  • Flask Bootstrap 后台权限管理方案
  • 【工具变量】企业数字化转型:数字化无形资产占比测算(2007-2024年)
  • 【算法基础课-算法模板2】数据结构
  • kmp复习,需要多看多练
  • 类和对象(前章)
  • 藏语识别技术在媒资行业的应用案例剖析