使用spring实现读写分离(mysql主从复制)四-优化改进切面实现,使用事务策略规则匹配

本文介绍了一种基于Spring框架实现数据库读写分离的方法,重点在于如何通过AOP切面技术来选择主库或从库进行操作。具体实现了根据不同事务策略或方法名前缀来决定数据源的选择。

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

之前的实现我们是将通过方法名匹配,不是使用事务策略中的定义,我们使用事务管理策略中的规则匹配,只需改动DataSourceAspect 切面类就可了

1.1. 改进后的配置

<!-- 定义AOP切面处理器-->

<bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect">

<!-- 指定事务策略 -->

<property name="txAdvice" ref="txAdvice"/>

<!-- 指定slave方法的前缀(非必须)-->

<property name="slaveMethodStart" value="query,find,get"/>

</bean>

1.2. 改进后的实现

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

 *

 * 如果没有配置事务管理的策略,则采用方法名匹配的原则,以queryfindget开头方法用Slave,其它用Master

 *

 * @author zhijun

 *

 */

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(TransactionInterceptortxAdvice) throws Exception {

        if (txAdvice ==null) {

            //没有配置事务管理策略

            return;

        }

        //txAdvice获取到策略配置信息

        TransactionAttributeSource transactionAttributeSource = txAdvice.getTransactionAttributeSource();

        if (!(transactionAttributeSource instanceof NameMatchTransactionAttributeSource)) {

            return;

        }

        //使用反射技术获取到NameMatchTransactionAttributeSource对象中的nameMap属性值

        NameMatchTransactionAttributeSourcematchTransactionAttributeSource = (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(JoinPointpoint) {

        //获取到当前执行的方法名

        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) {

        //方法名以queryfindget开头的方法名走从库

        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;

    }

    

}




上一篇   使用spring实现读写分离(mysql主从复制)三:使用spring实现读写分离



下一篇    使用spring实现读写分离(mysql主从复制)五:一主多从的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值