数据库读写分离(aop方式完整实现)

本文介绍了一种基于Spring框架的读写分离方案,通过自定义数据源类、数据源选择类及AOP切面实现数据库读写操作的有效分离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目要支持读写分离, 网上找了很多,但都是不太完整,我自己整理了下供大家参考。
我的项目使用的框架: springMvc+spring+hibernate+springJPA+maven, 数据库连接池用阿里的druid。


1. 新建一个DynamicDataSource类, 继承springAbstractRoutingDataSource 类,

并重写determineCurrentLookupKey()方法,如下: 

package com.dataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 数据源动态切换类
 * <p>
 * Copyright: Copyright (c) 2015-3-9 下午3:15:19
 * <p>
 * Company: 
 * <p>
 * 
 * @author macl@c-platform.com
 * @version 1.0.0
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceSwitcher.getDataSource();
	}

}

2. 新建DataSourceSwitcher类, 实现数据源选择:

package com.dataSource;

import org.springframework.util.Assert;

/**
 * 数据源选择类
 * <p>
 * Copyright: Copyright (c) 2015-3-9 下午3:14:55
 * <p>
 * Company:  
 * <p>
 * 
 * @author macl@c-platform.com
 * @version 1.0.0
 */
public class DataSourceSwitcher {

	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	/** 主库(写库) **/
	public static final String MASTER_DATA_SOURCE = "master";

	/** 从库(读库) **/
	public static final String SLAVE_DATA_SOURCE = "slave";

	public static void setDataSource(String dataSource) {
		Assert.notNull(dataSource, "dataSource cannot be null");
		contextHolder.set(dataSource);
	}

	public static void setMaster() {
		clearDataSource();
	}

	public static void setSlave() {
		setDataSource(SLAVE_DATA_SOURCE);
	}

	public static String getDataSource() {
		return (String) contextHolder.get();
	}

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

 

3. 新建切面类, 判断如果调用方法以query,list,get方法开头的, 切换为查询库: 

package com.dataSource;

import java.lang.reflect.Method;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

/**
 * 配置AOP切面类,动态切换读/写数据库。
 * <p>
 * Copyright: Copyright (c) 2015-3-9 下午3:16:51
 * <p>
 * Company:  
 * <p>
 * 
 * @author macl@c-platform.com
 * @version 1.0.0
 */
public class DataSourceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
 
	private static final Logger log = Logger.getLogger(DataSourceAdvice.class);

	private String logInfo;

	// 需要切换到从库(读库)的方法名前缀
	private List<String> slaveMethods;

	/**
	 * service方法执行之前被调用.
	 */
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		logInfo = String.format("before切入点:%s-->%s(),切换到:", target.getClass().getName(), method.getName());

		String methodName = method.getName();
		boolean hasSwitchedSlave = false;

		for (String slaveMethod : slaveMethods) {
			if (methodName.startsWith(slaveMethod)) {
				if (log.isDebugEnabled()) {
					log.debug(logInfo + DataSourceSwitcher.SLAVE_DATA_SOURCE);
				}
				hasSwitchedSlave = true;
				DataSourceSwitcher.setSlave();
				break;
			}
		}

		if (!hasSwitchedSlave) {
			if (log.isDebugEnabled()) {
				log.debug(logInfo + "切换到:" + DataSourceSwitcher.MASTER_DATA_SOURCE);
			}
			DataSourceSwitcher.setMaster();
		}
	}

	/**
	 * service方法执行完之后被调用.
	 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

	}

	/**
	 * 抛出Exception之后被调用。
	 * 
	 * @param method
	 * @param args
	 * @param target
	 * @param ex
	 * @throws Throwable
	 */
	public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
		logInfo = String.format("after throwing:%s类中%s方法,", target.getClass().getName(), method.getName());
		log.error(logInfo + "发生异常:" + ex.getMessage() + ",切换到:" + DataSourceSwitcher.SLAVE_DATA_SOURCE);
		DataSourceSwitcher.setSlave();
	}

	public List<String> getSlaveMethods() {
		return slaveMethods;
	}

	public void setSlaveMethods(List<String> slaveMethods) {
		this.slaveMethods = slaveMethods;
	}
}



4.  spring配置文件中, 配置数据源:

 

 

 

5. AOP配置(注意, 可以在service层切换(推荐), 也可以在控制器层切换):

截图为在控制层切换配置, 附件中的context.xml中给的是在service/dao层切换的配置方式。

6.  mvc层支持aop(如果aop切换配置在控制层, 则需要配置这步):


7. 特殊类可以硬编码:

      切换到主库 : DataSourceSwitcher.setMaster();

      比如 , 登录拦截器RoleGenInterceptor.java --> genLocalSession()



 附件下载地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值