Spring AOP 动态多数据源的实例详解

 Spring AOP 动态多数据源的实例详解

当项目中使用到读写分离的时候,我们就会遇到多数据源的问题。多数据源让人最头痛的,不是配置多个数据源,而是如何能灵活动态的切换数据源。例如在一个spring和Mybatis的框架的项目中,我们在spring配置中往往是配置一个dataSource来连接数据库,然后绑定给sessionFactory,在dao层代码中再指定sessionFactory来进行数据库操作。

正如上图所示,每一块都是指定绑死的,如果是多个数据源,也只能是下图中那种方式。

可看出在Dao层代码中写死了两个SessionFactory,这样日后如果再多一个数据源,还要改代码添加一个SessionFactory,显然这并不符合开闭原则。

那么正确的做法应该是:

具体代码与配置如下:

1、applicationContext-mgr.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  <!-- use annotation -->
  <context:annotation-config />
    <context:component-scan base-package="com.carl.o2o.**.mgr">
  </context:component-scan>

  <!-- master -->
  <bean id="master" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClassName_master}"/>
    <property name="user" value="${username_master}"/>
    <property name="password" value="${password_master}"/>
    <property name="jdbcUrl" value="${url_master}?Unicode=true&characterEncoding=UTF-8&allowMultiQueries=true"/>

    <property name="maxPoolSize" value="150"/>
    <property name="minPoolSize" value="10"/>
    <property name="initialPoolSize" value="20"/>
    <property name="maxIdleTime" value="3600"/>
    <property name="acquireIncrement" value="10"/>
    <property name="idleConnectionTestPeriod" value="1800"/>
  </bean>

  <!-- slave -->
  <bean id="slave" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClassName_slave}"/>
    <property name="user" value="${username_slave}"/>
    <property name="password" value="${password_slave}"/>
    <property name="jdbcUrl" value="${url_slave}?Unicode=true&characterEncoding=UTF-8"/>

    <property name="maxPoolSize" value="150"/>
    <property name="minPoolSize" value="10"/>
    <property name="initialPoolSize" value="20"/>
    <property name="maxIdleTime" value="3600"/>
    <property name="acquireIncrement" value="10"/>
    <property name="idleConnectionTestPeriod" value="1800"/>
  </bean>

  <!-- spring 动态数据源 -->
  <bean id="dynamicDataSource" class="com.carl.dbUtil.DynamicDataSource">
    <property name="targetDataSources">
      <map key-type="java.lang.String">
        <entry key="slave" value-ref="slave" />
      </map>
    </property>
    <property name="defaultTargetDataSource" ref="master" />
  </bean> 

  <!-- mybatis mapper config -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dynamicDataSource"/>
    <property name="configLocation" value="classpath:o2o_mybatis_config.xml"/>
    <property name="mapperLocations" >
      <list>
        <value>classpath:sqlMap/*.xml</value>
        <value>classpath*:/com/carl/o2o/**/*.xml</value>
      </list>
    </property>
  </bean>

  <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
  </bean>

  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.carl.o2o.**.mgr.dao" />
  </bean>

  <!-- 多数据源 aop -->
  <bean id="DataSourceAspect" class="com.carl.dbUtil.DataSourceAspect" />
  <aop:config>
    <aop:advisor pointcut="execution(* com.carl.o2o.mgr.*.*(..))" advice-ref="DataSourceAspect" />
  </aop:config> 

  <!-- 事务 -->
  <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dynamicDataSource"></property>
  </bean>
 </beans>

2、DynamicDataSource

DynamicDataSource使用Spring中的代码结合AOP实现多数据源切换.

public class DynamicDataSource extends AbstractRoutingDataSource {
  public DynamicDataSource() {
  }

  protected Object determineCurrentLookupKey() {
    return DBContextHolder.getDbType();
  }

  public Logger getParentLogger() {
    return null;
  }
}

3、DBContextHolder

DynamicDataSource的辅助类,用于实际的切换多数据源。

public class DBContextHolder {
  private static ThreadLocal<String> contextHolder = new ThreadLocal();
  public static String MASTER = "master";
  public static String SLAVE = "slave";

  public DBContextHolder() {
  }

  public static String getDbType() {
    String db = (String)contextHolder.get();
    if(db == null) {
      db = MASTER;
    }

    return db;
  }

  public static void setDbType(String str) {
    contextHolder.set(str);
  }

  public static void setMaster() {
    contextHolder.set(MASTER);
  }

  public static void setSlave() {
    contextHolder.set(SLAVE);
  }

  public static void clearDBType() {
    contextHolder.remove();
  }
}

4、DataSourceAspect

多数据源AOP切面编程实现。

public class DataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
  private static final Logger log = LogManager.getLogger(DataSourceAspect.class);

  public DataSourceAspect() {
  }

  public void before(Method m, Object[] args, Object target) throws Throwable {
    try {
      if(m != null) {
        if((m.getName().startsWith("list") || m.getName().startsWith("select") || m.getName().startsWith("get")
        || m.getName().startsWith("count")) && !m.getName().contains("FromMaster")) {
          DBContextHolder.setDbType("slave");
        } else {
          DBContextHolder.setDbType("master");
        }
      }
    } catch (Exception var5) {
      log.error("data source aspect error.", var5);
    }

  }

  public void after(JoinPoint point) {
    log.info("clear db type after method.current id {}", new Object[]{Long.valueOf(Thread.currentThread().getId())});
    DBContextHolder.clearDBType();
  }

  public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  }

  public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
    log.info("current db type {} when exception", new Object[]{DBContextHolder.getDbType()});
    DBContextHolder.setDbType("master");
  }
}

以上就是 Spring AOP 动态多数据源的实例详解,如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Spring AOP实现Redis缓存数据库查询源码

    应用场景 我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 需要解决的问题 操作缓存的代码写在哪?必须要做到与业务逻辑代码完全分离. 如何避免脏读? 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识?即通过该标识(Redis中为Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个key.只有这样才能保证缓存内容的正确性 如何序列化查询结果?查询结果可能是单个实体

  • Spring+Mybatis 实现aop数据库读写分离与多数据库源配置操作

    在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力. 废话不多说,多数据源配置和主从数据配置原理一样 1.首先配置  jdbc.prope

  • 详解SpringBoot AOP 拦截器(Aspect注解方式)

    常用用于实现拦截的有:Filter.HandlerInterceptor.MethodInterceptor 第一种Filter属于Servlet提供的,后两者是spring提供的,HandlerInterceptor属于Spring MVC项目提供的,用来拦截请求,在MethodInterceptor之前执行. 实现一个HandlerInterceptor可以实现接口HandlerInterceptor,也可以继承HandlerInterceptorAdapter类,两种方法一样.这个不在本文

  • spring aop action中验证用户登录状态的实例代码

    最近在学习ssh框架时,照着网上做了一个商城系统,之前在一些需要用户存在的操作中,都是在每一个action中写重复的代码,这样做现在想起来并不好,想起了spring的aop,于是想通过aop来给每个需要用户操作的Action验证用户登录状态. 想法是这样的: 1. 用户登录时把userId放入session中 2. 通过spring 写一个advice来获取session中的userId,判断用户登录状态,如果userId不符合,则抛出自定义异常 3. 通过struts中配置来捕获异常,跳转界面

  • 利用spring AOP记录用户操作日志的方法示例

    前言 最近项目已经开发完成,但发现需要加用户操作日志,如果返回去加也不太现实,所以使用springAOP来完成比较合适.下面来一起看看详细的介绍: 注解工具类: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogAnnotation { String operateModelNm() default ""; String operateFuncNm() default

  • Spring AOP 动态多数据源的实例详解

     Spring AOP 动态多数据源的实例详解 当项目中使用到读写分离的时候,我们就会遇到多数据源的问题.多数据源让人最头痛的,不是配置多个数据源,而是如何能灵活动态的切换数据源.例如在一个spring和Mybatis的框架的项目中,我们在spring配置中往往是配置一个dataSource来连接数据库,然后绑定给sessionFactory,在dao层代码中再指定sessionFactory来进行数据库操作. 正如上图所示,每一块都是指定绑死的,如果是多个数据源,也只能是下图中那种方式. 可看

  • 基于Spring + Spring MVC + Mybatis 高性能web构建实例详解

    一直想写这篇文章,前段时间痴迷于JavaScript.NodeJs.AngularJS,做了大量的研究,对前后端交互有了更深层次的认识. 今天抽个时间写这篇文章,我有预感,这将是一篇很详细的文章,详细的配置,详细的注释,看起来应该很容易懂. 用最合适的技术去实现,并不断追求最佳实践.这就是架构之道. 希望这篇文章能给你们带来一些帮助,同时希望你们可以为这个项目贡献你的想法. 源码地址:https://github.com/Eliteams/quick4j 点击打开 源码地址:https://gi

  • Spring boot注解@Async线程池实例详解

    这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法.调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行. 1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent

  • Spring AOP的概念与实现过程详解

    目录 Aop 实现aop方式一 实现aop方式二 注解实现aop Aop 什么是Aop? AOP就是面向切面编程,通过预编译方式以及运行期间的动态代理技术来实现程序的统一维护功能. 什么是切面,我理解的切面就是两个方法之间,两个对象之间,两个模块之间就是一个切面.假设在两个模块之间需要共同执行一系列操作,并且最后将这一系列操作注入到两个模块之间的指定位置.此时这一系列操作就是切面,注入这些操作的位置称之为切点. 举例:公司员工上班 A员工上班需要在前台进行打卡,同样的B员工…其他员工都需要在前台

  • Spring mvc 分步式session的实例详解

    Spring mvc 分步式session的实例详解 Session代表服务器与浏览器的一次会话过程,它的信息是保存在服务器端的.在Servlet中,session指的是HttpSession类的对象.服务器在创建session后,会把sessionid以cookie的形式回写给客户端.只要客户端的浏览器不关,每一次访问服务器都会带上这个sessionid.这样就可以在每次请求的时候获取到session的信息. 下面以spring MVC以例来说明如果创建分步式session. 1.login

  • JSP 中Spring组合注解与元注解实例详解

    JSP 中Spring组合注解与元注解实例详解 摘要: 注解(Annotation),也叫元数据.一种代码级别的说明.它与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明 1. 可以注解到别的注解上的注解称为元注解,被注解的注解称为组合注解,通过组合注解可以很好的简化好多重复性的注解操作 2. 示例组合注解 import org.springframework.context.annotation.ComponentScan; im

  • Android ListView中动态添加RaidoButton的实例详解

    Android ListView中动态添加RaidoButton的实例详解 这里讲解的内容是:从数据库中取得数据,将这些数据的value值赋值给Radiobutton的text属性,将这些数据的key值赋值给radiobutton的key值.同时实现点击一整行,更换radiobutton选择. XML代码:主要是添加一个ListView控件 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&q

  • Spring MVC自定义日期类型转换器实例详解

    Spring MVC自定义日期类型转换器实例详解 WEB层采用Spring MVC框架,将查询到的数据传递给APP端或客户端,这没啥,但是坑的是实体类中有日期类型的属性,但是你必须提前格式化好之后返回给它们.说真的,以前真没这样做过,之前都是一口气查询到数据,然后在jsp页面上格式化,最后展示给用户.但是这次不同,这次我纯属操作数据,没有页面.直接从数据库拿数据给它们返数据.它们给我传数据我持久化数据,说到这里一个小问题就默默的来了. 首先把问题还原一下吧(这是一个数据导出功能),下图中用红框圈

  • MyBatis动态SQL标签用法实例详解

    1.动态SQL片段 通过SQL片段达到代码复用 <!-- 动态条件分页查询 --> <sql id="sql_count"> select count(*) </sql> <sql id="sql_select"> select * </sql> <sql id="sql_where"> from icp <dynamic prepend="where&quo

  • 基于 D3.js 绘制动态进度条的实例详解

    D3 是什么 D3 的全称是(Data-Driven Documents),顾名思义可以知道是一个被数据驱动的文档.听名字有点抽象,说简单一点,其实就是一个 JavaScript 的函数库,使用它主要是用来做数据可视化的.如果你不知道什么是 JavaScript ,请先学习一下 JavaScript,推荐阮一峰老师的教程. JavaScript 文件的后缀名通常为 .js,故 D3 也常使用 D3.js 称呼.D3 提供了各种简单易用的函数,大大简化了 JavaScript 操作数据的难度.由于

随机推荐