美文网首页Java专题MySQL
使用Spring配置动态数据源实现读写分离

使用Spring配置动态数据源实现读写分离

作者: 简单的土豆 | 来源:发表于2016-06-28 21:21 被阅读2912次

最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考。
关键词:DataSource 、AbstractRoutingDataSource、AOP

首先是配置数据源


<!--读数据源配置-->
<bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"destroy-method="close">
//配置省略
</bean>

<!--写数据源配置-->
<bean id="writeDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"destroy-method="close">
//配置省略
</bean>

<!-- 动态数据源 -->
<bean id = "dataSource" class="com.potato.common.bean.DynamicDataSource" > 
  <!-- 已配置的数据源 -->   
 <property name="targetDataSources">      
  <map>          
      <entry key="READ" value-ref="readDataSource"/>          
      <entry key="WRITE" value-ref="writeDataSource"/>        
 </map>   
 </property>   
 <!-- 默认的数据源 -->  
 <property name="defaultTargetDataSource" ref="writeDataSource"/>
</bean>

数据源是如何切换的呢?通过动态数据源的配置我们知道原来是通过key来进行切换,这里要使用到org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,我们可以编写自己的动态数据源类DynamicDataSource来继承它。

public class DynamicDataSource extends AbstractRoutingDataSource {  
  @Override    
  protected Object determineCurrentLookupKey() {       
     return DataSourceContextHolder.getType();    
    }
}

还需要一个存放key的地方DataSourceContextHolder 为保证切换时线程安全我们使用ThreadLocal来保存我们的key。

public class DataSourceContextHolder {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder.class);
    public static final String DATA_SOURCE_WRITE = "WRITE";
    public static final String DATA_SOURCE_READ = "READ";
    // 线程本地环境
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    // 设置数据源类型
    public static void setType(String type) {
        if(LOGGER.isDebugEnabled())
            LOGGER.debug("==============切换数据源,类型:"+type+"================");
        contextHolder.set(type);
    }
    // 获取数据源类型
    public static String getType() {
        return (contextHolder.get());
    }
    // 清除数据源类型
    public static void clearType() {
        contextHolder.remove();
    }
}

好了,我们可以通过操作DataSourceContextHolder来实现数据源动态的切换了。小伙伴们可能会说了,难道每次调用方法都要手动选择要切换的数据源类型?当然不是啦,Spring AOP登场。

@Component
@Aspect
public class DynamicDataSourceAspect {

    @Pointcut("execution (* com.potato.orm.mapper.*.select*(..)) || execution (* com.potato.orm.mapper.*.count*(..)) ")
    public void readMethodPointcut() {}

    @Pointcut("execution (* com.potato.orm.mapper.*.insert*(..)) || execution (* com.potato.orm.mapper.*.update*(..)) || execution (* com.potato.orm.mapper.*.delete*(..))")
    public void writeMethodPointcut() {}

    @Before("readMethodPointcut()")
    public void switchReadDataSource(){
        //System.out.println("============切换到读数据源===========");
        DataSourceContextHolder.setType(DataSourceContextHolder.DATA_SOURCE_READ);
    }

    @Before("writeMethodPointcut()")
    public void switchWriteDataSource(){
        //System.out.println("=============切换到写数据源==========");
        DataSourceContextHolder.setType(DataSourceContextHolder.DATA_SOURCE_WRITE);
    }

}

好啦,在访问Mapper(本项目使用的是MyBatis啦,相当于是DAO)中查询方法时会切换到读数据源,增、删、改方法会切换到写数据源。

相关文章

网友评论

  • BJ你若盛开:代码地址发下 多谢
  • Nathans:你好,我和你配置相同,但始终最先执行的是事物,导致无法切换
    c3747309e199:遇到同样的问题,事务有问题
  • f22a4626ccf4:按照你的配置,为什么没起作用,log是显示切换了,但是账号没有切换,请问是什么原因啊?
  • leeyaf:不是有系统级别的软件的吗?这种的话多slave就不好搞了吧
    简单的土豆:小项目而已:relieved:,这样够用了,省事。

    要做集群的话最蠢得办法就是配置多个读数据源,通过算法来均衡。最好是那个读数据源通过系统级别软件做负载均衡。
    leeyaf:@leeyaf 而且slave增量或者下线你这边没办法感知

本文标题:使用Spring配置动态数据源实现读写分离

本文链接:https://www.haomeiwen.com/subject/ckitjttx.html