spring 配置双数据源并读写分离
摘自 开源项目Ibase4j
关键思想在于AbstractRoutingSource 类 还有方法名称和切入点去控制使用哪个数据源
1.首先在配置文件配置多个数据源 并且交给继承自spring AbstractRoutingSource去管理
datasource.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter" lazy-init="true">
<description>状态过滤器</description>
<property name="slowSqlMillis" value="3000" />
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean>
<bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init" lazy-init="true">
<description>只读数据库连接</description>
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.reader.url}" />
<property name="username" value="${db.reader.username}" />
<property name="password" value="${db.reader.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${db.initialSize}" />
<!-- 连接池最大数量 -->
<property name="maxActive" value="${db.maxActive}" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${db.minIdle}" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${db.maxWait}" />
<!-- -->
<property name="defaultReadOnly" value="true" />
<property name="proxyFilters">
<list>
<ref bean="stat-filter" />
</list>
</property>
<property name="filters" value="${druid.filters}" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="timeBetweenLogStatsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}" />
</bean>
<bean id="writeDataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init" lazy-init="true">
<description>只写数据库连接</description>
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.writer.url}" />
<property name="username" value="${db.writer.username}" />
<property name="password" value="${db.writer.password}" />
<property name="initialSize" value="${db.initialSize}" />
<property name="maxActive" value="${db.maxActive}" />
<property name="minIdle" value="${db.minIdle}" />
<property name="maxWait" value="${db.maxWait}" />
<property name="defaultReadOnly" value="false" />
<property name="proxyFilters">
<list>
<ref bean="stat-filter" />
</list>
</property>
<property name="filters" value="${druid.filters}" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="timeBetweenLogStatsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}" />
<property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}" />
</bean>
<!--注意这里的配置-->
<bean id="dataSource" class="org.ibase4j.core.aspect.ChooseDataSource" lazy-init="true">
<description>数据源</description>
<property name="targetDataSources">
<map key-type="java.lang.String" value-type="javax.sql.DataSource">
<!-- write -->
<entry key="write" value-ref="writeDataSource" />
<!-- read -->
<entry key="read" value-ref="readDataSource" />
</map>
</property>
<property name="defaultTargetDataSource" ref="writeDataSource" />
<property name="methodType">
<map key-type="java.lang.String">
<!-- read -->
<entry key="read" value=",get,select,count,list,query," />
<!-- write -->
<entry key="write" value=",add,insert,create,update,delete,remove," />
</map>
</property>
</bean>
<!-- 注意这个切面 -->
<bean class="org.ibase4j.core.aspect.DataSourceAspect" />
<!-- -->
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
</beans>
接下来事继承类
package org.ibase4j.core.aspect; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /**
* 获取数据源
*
* @author ShenHuaJie
* @version 2016年5月20日 下午3:17:16
*/
public class ChooseDataSource extends AbstractRoutingDataSource {
public static Map<String, List<String>> METHODTYPE = new HashMap<String, List<String>>(); // 获取数据源名称
protected Object determineCurrentLookupKey() {
return HandleDataSource.getDataSource();
} // 设置方法名前缀对应的数据源
public void setMethodType(Map<String, String> map) {
for (String key : map.keySet()) {
List<String> v = new ArrayList<String>();
String[] types = map.get(key).split(",");
for (String type : types) {
if (StringUtils.isNotBlank(type)) {
v.add(type);
}
}
METHODTYPE.put(key, v);
}
}
}
接下来切面实现类
package org.ibase4j.core.aspect; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy; /**
* 切换数据源(不同方法调用不同数据源)
*
* @author ShenHuaJie
* @version 2016年5月20日 下午3:17:52
*/
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect {
private final Logger logger = LogManager.getLogger();
//配置切入点
@Pointcut("execution(* org.ibase4j.service..*.*(..))")
public void aspect() {
} /**
* 配置前置通知,使用在方法aspect()上注册的切入点
*/
@Before("aspect()")
public void before(JoinPoint point) {
String className = point.getTarget().getClass().getName();
String method = point.getSignature().getName();
logger.info(className + "." + method + "(" + StringUtils.join(point.getArgs(), ",") + ")");
try {
L: for (String key : ChooseDataSource.METHODTYPE.keySet()) {
for (String type : ChooseDataSource.METHODTYPE.get(key)) {
if (method.startsWith(type)) {
logger.info(key);
HandleDataSource.putDataSource(key);
break L;
}
}
}
} catch (Exception e) {
logger.error(e);
HandleDataSource.putDataSource("write");
}
} @After("aspect()")
public void after(JoinPoint point) {
HandleDataSource.clear();
}
}
还有一个帮助你拿到数据元的类
package org.ibase4j.core.aspect; /**
* @author ShenHuaJie
* @version 2016年5月20日 下午3:18:04
*/
public class HandleDataSource {
// 数据源名称线程池
private static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String datasource) {
holder.set(datasource);
} public static String getDataSource() {
return holder.get();
} public static void clear() {
holder.remove();
}
}
spring 配置双数据源并读写分离的更多相关文章
- 使用Spring配置动态数据源实现读写分离
最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考.关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!-- ...
- 阿里P7教你如何使用 Spring 配置动态数据源实现读写分离
最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考. 关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!- ...
- 使用 Spring 配置动态数据源实现读写分离
关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!--读数据源配置--><bean id="readData ...
- mybatis用spring的动态数据源实现读写分离
一.环境: 三个mysql数据库.一个master,两个slaver.master写数据,slaver读数据. 二.原理: 借助Spring的 AbstractRoutingDataSource 这个 ...
- Spring Boot2(四):使用Spring Boot多数据源实现读写分离
前言 实际业务场景中,不可能只有一个库,所以就有了分库分表,多数据源的出现.实现了读写分离,主库负责增改删,从库负责查询.这篇文章将实现Spring Boot如何实现多数据源,动态数据源切换,读写分离 ...
- spring项目配置双数据源读写分离
我们最早做新项目的时候一直想做数据库的读写分离与主从同步,由于一些原因一直没有去做这个事情,这次我们需要配置双数据源的起因是因为我们做了一个新项目用了另一个数据库,需要把这个数据库的数据显示到原来的后 ...
- Spring配置动态数据源-读写分离和多数据源
在现在互联网系统中,随着用户量的增长,单数据源通常无法满足系统的负载要求.因此为了解决用户量增长带来的压力,在数据库层面会采用读写分离技术和数据库拆分等技术.读写分离就是就是一个Master数据库,多 ...
- 原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
开心一刻 女孩睡醒玩手机,收到男孩发来一条信息:我要去跟我喜欢的人表白了! 女孩的心猛的一痛,回了条信息:去吧,祝你好运! 男孩回了句:但是我没有勇气说不来,怕被打! 女孩:没事的,我相信你!此时女孩 ...
- 170301、使用Spring AOP实现MySQL数据库读写分离案例分析
使用Spring AOP实现MySQL数据库读写分离案例分析 原创 2016-12-29 徐刘根 Java后端技术 一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案 ...
随机推荐
- height 与 min-height 的继承
min-height: inherit; 继承父元素的 min-height: 80px; 但,不能继承父元素的 height: 200px; height: inherit; 能继承父元素的: he ...
- IOS开发 Application Kit框架的线程安全
以下部分介绍了Application Kit框架的线程安全. 非线程安全类 以下这些类和函数通常是非线程安全的.大部分情况下,你可以在任何线程使用这些类,只要你在同一时间只有一个线程使用它们.查看这些 ...
- Compass入门
一.Compass是什么? 简单说,Compass是Sass的工具库(toolkit). Sass本身只是一个编译器,Compass在它的基础上,封装了一系列有用的模块和模板,补充Sass的功能. ...
- Mac终端建立替身 并置于桌面或Finder中
前情 Xcode存放log的文件夹路径忒长了,且需要用终端才能查看.所以就想制作个替身,放在Finder中便于查看. going on command+space打开terminal 一直cd...进 ...
- 使用dlopen和dlsym来使用C++中的类
http://my.oschina.net/u/1450061/blog/204608
- 21天学通C++_Day5
昨天停更了一天,真是羞羞啊,不过还是干了很多有意义的事的! 首先,昨天下午的时候,去参加了学校的春招!第一次参加招聘会,怕自己答不上面试官的问题,很是紧张! 和同学约的一点,结果到了发现还没开始,只能 ...
- linux自学(四)之开始centos学习,网络配置
上一篇:linux自学(三)之开启虚拟机 安装好镜像之后,重启之后需要登录,我这里直接是root账号直接登录的,注意:输入密码的时候不显示. 之后输入ifconfig最常用的命令来查看网卡信息,出现c ...
- Servlet实现文件上传和下载
对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具commo ...
- BZOJ1002 FJOI2007 轮状病毒 【基尔霍夫矩阵+高精度】
BZOJ1002 FJOI2007 轮状病毒 Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子和圆心处一个核原子构成的,2个原 ...
- 《DSP using MATLAB》示例Example 8.1
终于看到第8章了,加油,继续努力! N为奇数,有极点位于实数轴上.事实上,所有极点位于Ωc=0.5为半径的圆上,而不是单位圆.