星空网 > 软件开发 > Java

springAOP实现基于注解的数据源动态切换

需求

代码实现读写数据库分离

武器

spring3.0以上版本

实现思路

1、继承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,自定义数据源路由。

2、实现数据源类型管理工具,诸如DBContextHolder,包含设置和读取当前数据源配置。

3、实现数据源切换的AOP。

4、自定义只读注解,诸如@ReadOnlyKey。

5、配置transactionManager,实现aop。

代码示例

1、自定义的DynamicDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {  /**   * 自动查找数据源   *   * @return 数据源名   */  @Override  protected Object determineCurrentLookupKey() {    String dataSource = getDataSource();    return dataSource;  }}

2、数据源类型管理工具DBContextHolder

public abstract class DBContextHolder {  /**   * 数据源类型管理   * <p>   * 考虑多线程,为保证线程之间互不干扰,所以使用ThreadLocal作线程隔离;<br>   * 参数是数据源键值   * </p>   *   * @see ThreadLocal   */  private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();  /**   * 数据库源类型   * <p>   * 配置数据源的时候,请遵守以下约束:<br>   * 读写:dataSourceKeyRW;<br>   * 读:dataSourceKeyR.   * </p>   */  public enum DbType {    DB_TYPE_RW("dataSourceKeyRW"), DB_TYPE_R("dataSourceKeyR");    private String dataSourceKey;    DbType(String dataSourceKey) {      this.dataSourceKey = dataSourceKey;    }    public String getDataSourceKey() {      return dataSourceKey;    }  }  /**   * 获取数据源   * <p>   * 如果未设置,默认返回读数据源   * </p>   *   * @return 数据源键值   */  public static String getDataSource() {    String dataSource = contextHolder.get();    if (StringUtils.isEmpty(dataSource)) {      dataSource = DbType.DB_TYPE_RW.dataSourceKey;    }    return dataSource;  }  /**   * 设置数据源   *   * @param dataSourceKey 数据源键值   */  public static void setDataSource(String dataSourceKey) {    contextHolder.set(dataSourceKey);  }}

注:定义了DbType枚举,分别定义了读和写的数据源键值。

3、实现AOP。

public class DataSourceSwitchingAop {  /**   * 设置切点数据源   * <p>   * 调试输出数据源.   * </p>   *   * @param joinPoint   切点   * @param dataSourceKey 当前数据源键值   */  private void setDataSourceByKey(JoinPoint joinPoint, String dataSourceKey) {    setDataSource(dataSourceKey);    debugLog(joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName() + "配置数据源:" + getDataSource());  }  /**   * 切换数据源   * <p>   * 切换优先级由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,类上注解DataSourceKey;<br>   * 如果未注解,则默认设置写数据源.   * </p>   *   * @param joinPoint 切点   * @see DataSourceKey   * @see ReadOnlyKey   * @see DbType   */  public void switchDataSource(JoinPoint joinPoint) {    Class<?> targetClass = joinPoint.getTarget().getClass();    String methodName = joinPoint.getSignature().getName();    Object[] args = joinPoint.getArgs();    DataSourceKey dataSourceKey = getAnnotationClassMethod(targetClass, methodName, DataSourceKey.class, args);    if (dataSourceKey != null) {      setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());      return;    }    ReadOnlyKey readOnlyKey = getAnnotationClassMethod(targetClass, methodName, ReadOnlyKey.class, args);    if (readOnlyKey != null) {      setDataSourceByKey(joinPoint, DbType.DB_TYPE_R.getDataSourceKey());      return;    }    dataSourceKey = (DataSourceKey) targetClass.getAnnotation(DataSourceKey.class);    if (dataSourceKey != null) {      setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());      return;    }    setDataSourceByKey(joinPoint, DbType.DB_TYPE_RW.getDataSourceKey());  }}

4、自定义只读注解,@ReadOnlyKey

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface ReadOnlyKey {}

5、配置transaction和AOP

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    <property name="dataSource" ref="dynamicDataSource"/>  </bean>

<bean id="dataSourceSwitchingAop" class="com.xxx.common.framework2x.dao.DataSourceSwitchingAop"/>

<aop:config>    <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">      <aop:pointcut id="dataSourceSwitchingService"             expression="execution(* com.xxx.manager..*.*(..))"/>      <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>    </aop:aspect>  </aop:config>

以上就完成了基于注解实现动态切换读写数据源。

6、如果想要实现多数据源的切换,则可以自定义注解@DataSourceKey

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD, ElementType.TYPE})public @interface DataSourceKey {  /**   * 配置数据源键值   * <p>   * 默认:dataSource.   * </p>   *   * @return 键值   */  String dataSourceKey() default "dataSource";}

接口方法上增加注解即可。

需要特别注意的地方

1、切换数据源的事务需要放到数据库事务开启前执行。针对上述代码示例中,配置aop时需要指定order(值越小,执行越靠前)

<aop:config>    <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">      <aop:pointcut id="dataSourceSwitchingService"             expression="execution(* com.xxx.manager..*.*(..))"/>      <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>    </aop:aspect>  </aop:config>

2、@DataSourceKey可以加在method上,也可以加到class上,优先级是method>class。

3、@ReadOnlyKey只能加到method上。

4、@DatasourceKey和@ReadOnlyKey可以在一个class中混用,优先级是method的@DatasourceKey>method的@ReadOnlyKey>class的@DatasourceKey。

 




原标题:springAOP实现基于注解的数据源动态切换

关键词:Spring

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

避坑指南:亚马逊UPC、EAN等GTIN的那些事!:https://www.ikjzd.com/articles/111859
亚马逊大咖纯干货分享:亚马逊新卖家如何做到月入五万?:https://www.ikjzd.com/articles/11186
跨境电商论坛:走进非洲,共建“一带一路”跨境发展!:https://www.ikjzd.com/articles/111861
怎样做好亚马逊测评,规避风险?:https://www.ikjzd.com/articles/111862
阿里国际站橱窗权益全面升级,你还在等什么?:https://www.ikjzd.com/articles/111863
复盘 : 十一月的跨境实操运营笔记!:https://www.ikjzd.com/articles/111864
37号文今后是否会更新?一文详解关键信息 :https://www.kjdsnews.com/a/1836441.html
探讨内地人开设香港账户的可行性 :https://www.kjdsnews.com/a/1836442.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流