基于spring源码实现数据库读写分离

该博客介绍基于Spring源码通过AOP实现读写分离的思路。先有请求,接着进行目标方法前置增强,根据方法名称类型设置不同key,再依据这些key选择不同类型的数据库,最后配置AOP声明式事务。还提到determineTargetDataSource()的调用时机及可优化细节。

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

其实就是通过AOP实现:
思路:
请求---目标方法前置增强---选择数据库类型(读写数据库类型选择)
1.请求
2.前置增强:根据根据方法名称类型,设置不同key
3.实现数据库的选择:根据(2)设置的不同的key,选择不同类型的数据库
4.配置AOP声明式事务


1.略

2.前置增强:根据根据方法名称类型,设置不同key

public class DataSourceMethodBeforeAdvice implements MethodBeforeAdvice {

    private MultiDataSource multiDataSource;

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        //1.判断方法名类型
        String methodName = method.getName();

        //2.设置该次请求要选择的DataSource类型的key
        if (StringUtils.startsWith(methodName,"add")
                || StringUtils.startsWith(methodName,"insert")
                || StringUtils.startsWith(methodName,"delete")) {
            multiDataSource.setDataSourceKey("master");
        } else {
            multiDataSource.setDataSourceKey("slave1");
        }

    }

    public void setMultiDataSource(MultiDataSource multiDataSource) {
        this.multiDataSource = multiDataSource;
    }
}

3.实现数据库的选择:根据(2)设置的不同的key,选择不同类型的数据库

public class MultiDataSource extends AbstractRoutingDataSource {

    private String dataSourceKey;

    @Override
    protected Object determineCurrentLookupKey() {

        return dataSourceKey;
    }

    public void setDataSourceKey(String dataSourceKey) {
        System.out.println("=============DataSourceKey:"+dataSourceKey);
        this.dataSourceKey = dataSourceKey;
    }
}

4.配置AOP声明式事务

    <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean
        below) 事务传播特性配置 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>
            <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>
            <tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>
            <tx:method name="modify*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>
            <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.Exception"/>

            <!-- 查询方法 -->
            <tx:method name="query*" read-only="true"/>
            <tx:method name="select*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
        </tx:attributes>
    </tx:advice>


    <!-- 配置事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="cn.test.aop.DataSourceMethodBeforeAdvice" id="dataSourceMethodBeforeAdvice">
        <property name="multiDataSource" ref="dataSource"/>
    </bean>

    <!-- 声明式事务AOP配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.test.service.impl.*.*(..))" id="tranpointcut"/>
        <aop:advisor advice-ref="dataSourceMethodBeforeAdvice" pointcut-ref="tranpointcut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="tranpointcut"/>
    </aop:config>

<bean class="cn.test.datasource.MultiDataSource" id="dataSource">
        <!-- 设置默认数据源 -->
        <property name="defaultTargetDataSource" ref="masterDataSource1"/>
        <!-- 设置所有的数据源与其对应的key -->
        <property name="targetDataSources">
            <map>
                <entry key="master" value-ref="masterDataSource1"/>
                <entry key="slave1" value-ref="slaveDataSource1"/>
                <entry key="slave2" value-ref="slaveDataSource2"/>
            </map>
        </property>
    </bean>

    <!-- 数据库基本信息配置 -->
    <bean id="masterDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.pwd}"/>
    </bean>

    <bean id="slaveDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.pwd}"/>
    </bean>

    <bean id="slaveDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.pwd}"/>
    </bean>

需要注意的是determineTargetDataSource()是在需要的时候才会被调用,即如上配置,service有事物控制的,则从service开始调用,没有事物控制的则从mapper开始调用。

有一些细节可以优化,这里只做简单使用举例说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值