1. 程式人生 > >MySQL讀寫分離事務策略實現

MySQL讀寫分離事務策略實現

事務策略

之前的實現,是將通過方法名匹配
而不是使用事務策略中的定義

可以使用事務管理策略中的規則匹配
需要修改兩個地方

定義切面
定義資料來源的AOP切面

定義切面

<!-- 定義AOP切面處理器 -->
<bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect">
    <!-- 指定事務策略 -->
    <property name="txAdvice" ref="txAdvice" />
    <!-- 指定slave方法的字首(非必須) -->
<property name="slaveMethodStart" value="query,find,get" /> </bean>

DataSourceAspect

定義資料來源的AOP切面

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import
org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; import org.springframework.transaction.interceptor.TransactionInterceptor; import
org.springframework.util.PatternMatchUtils; import org.springframework.util.ReflectionUtils; /** * 定義資料來源的AOP切面,該類控制了使用Master還是Slave。 * * 如果事務管理中配置了事務策略,則採用配置的事務策略中的標記了ReadOnly的方法是用Slave,其它使用Master。 * * 如果沒有配置事務管理的策略,則採用方法名匹配的原則,以query、find、get開頭方法用Slave,其它用Master。 * */ public class DataSourceAspect { private List<String> slaveMethodPattern = new ArrayList<String>(); private static final String[] defaultSlaveMethodStart = new String[]{ "query", "find", "get" }; private String[] slaveMethodStart; /** * 讀取事務管理中的策略 * * @param txAdvice * @throws Exception */ @SuppressWarnings("unchecked") public void setTxAdvice(TransactionInterceptor txAdvice) throws Exception { if (txAdvice == null) { // 沒有配置事務管理策略 return; } //從txAdvice獲取到策略配置資訊 TransactionAttributeSource transactionAttributeSource = txAdvice.getTransactionAttributeSource(); if (!(transactionAttributeSource instanceof NameMatchTransactionAttributeSource)) { return; } //使用反射技術獲取到NameMatchTransactionAttributeSource物件中的nameMap屬性值 NameMatchTransactionAttributeSource matchTransactionAttributeSource = (NameMatchTransactionAttributeSource) transactionAttributeSource; Field nameMapField = ReflectionUtils.findField(NameMatchTransactionAttributeSource.class, "nameMap"); nameMapField.setAccessible(true); //設定該欄位可訪問 //獲取nameMap的值 Map<String, TransactionAttribute> map = (Map<String, TransactionAttribute>) nameMapField.get(matchTransactionAttributeSource); //遍歷nameMap for (Map.Entry<String, TransactionAttribute> entry : map.entrySet()) { if (!entry.getValue().isReadOnly()) {//判斷之後定義了ReadOnly的策略才加入到slaveMethodPattern continue; } slaveMethodPattern.add(entry.getKey()); } } /** * 在進入Service方法之前執行 * * @param point 切面物件 */ public void before(JoinPoint point) { // 獲取到當前執行的方法名 String methodName = point.getSignature().getName(); boolean isSlave = false; if (slaveMethodPattern.isEmpty()) { // 當前Spring容器中沒有配置事務策略,採用方法名匹配方式 isSlave = isSlave(methodName); } else { // 使用策略規則匹配 for (String mappedName : slaveMethodPattern) { if (isMatch(methodName, mappedName)) { isSlave = true; break; } } } if (isSlave) { // 標記為讀庫 DynamicDataSourceHolder.markSlave(); } else { // 標記為寫庫 DynamicDataSourceHolder.markMaster(); } } /** * 判斷是否為讀庫 * * @param methodName * @return */ private Boolean isSlave(String methodName) { // 方法名以query、find、get開頭的方法名走從庫 return StringUtils.startsWithAny(methodName, getSlaveMethodStart()); } /** * 萬用字元匹配 * * Return if the given method name matches the mapped name. * <p> * The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, as well as direct * equality. Can be overridden in subclasses. * * @param methodName the method name of the class * @param mappedName the name in the descriptor * @return if the names match * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) */ protected boolean isMatch(String methodName, String mappedName) { return PatternMatchUtils.simpleMatch(mappedName, methodName); } /** * 使用者指定slave的方法名字首 * @param slaveMethodStart */ public void setSlaveMethodStart(String[] slaveMethodStart) { this.slaveMethodStart = slaveMethodStart; } public String[] getSlaveMethodStart() { if(this.slaveMethodStart == null){ // 沒有指定,使用預設 return defaultSlaveMethodStart; } return slaveMethodStart; } }