Spring上设置mysql数据源动态切换(master写、slaver读)

一般情况下,很多项目(spring)由于对数据库(mysql)的并发量上来之后,都会配置主从机数据库,写数据使用主机数据库,而读数据使用从机数据库。那么如何使得对主从机数据源的使用能动态根据业务切换?

一、一些配置和依赖

1、一些需要的jar包

由于我这边使用了maven,也用到了aspect、mysql,so附上maven依赖,方便copy。

<properties>
    <spring.version>4.2.2.RELEASE</spring.version>
    <aspectj.version>1.6.11</aspectj.version>
    <mysql_connect_version>5.1.35</mysql_connect_version>
</properties>
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql_connect_version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
     <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--使用AspectJ方式注解需要相应的包-->  
    <dependency>  
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjrt</artifactId>  
        <version>${aspectj.version}</version>  
    </dependency>  
     <!--使用AspectJ方式注解需要相应的包-->  
    <dependency>  
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjweaver</artifactId>  
        <version>${aspectj.version}</version>  
    </dependency>
</dependencies>

2、spring配置(数据源和动态选择类)

 <!-- 主机数据库数据源配置 -->
<bean id="dataSourceMaster">
    <!-- 略过若干配置,我相信来看这篇文章的兄弟应该不会不懂吧 -->
</bean>

<bean id="dataSourceSlave">
       <!-- 略过若干配置,我相信来看这篇文章的兄弟应该不会不懂吧 -->
</bean>
 <!-- 配置动态分配的读写 数据源 -->
 <!-- 该ChooseDataSource类需要继承AbstractRoutingDataSource类并重写determineCurrentLookupKey方法,determineCurrentLookupKey方法主要用于从ThreadLocal变量中获取数据源的key -->
<bean id="dataSource" class="ChooseDataSource" lazy-init="true">
    <property name="targetDataSources">
        <map key-type="java.lang.String" value-type="javax.sql.DataSource">
            <!-- write -->
            <entry key="master" value-ref="dataSourceMaster"/>
            <!-- read -->
            <entry key="slave" value-ref="dataSourceSlave"/>
        </map>
    </property>
    <!-- 配置一个默认的使用数据源 -->
    <property name="defaultTargetDataSource" ref="dataSourceMaster"/>
    <!-- 给一个Map<String,String>注入选择源策略 -->
    <!-- 后面的切面方法(aspect())会判断执行的Dao的方法名字是否包含Map的value中的字符子串,包含即把对应的key(数据源名称)使用ThreadLocal把key存到该Dao的方法执行线程的ThreadMap里,那么ChooseDataSource类重写的determineCurrentLookupKey方法就会调用ThreadLocal把key从执行线程的ThreadMap取出来,就实现了一种动态切换的效果了 -->
    <property name="methodType">
        <map key-type="java.lang.String">
            <!-- read -->
            <entry key="slave">
                <list>
                    <value>get</value>
                    <value>select</value>
                    <value>find</value>
                </list>
            </entry>
            <!-- write -->
            <entry key="master">
                <list>
                    <value>add</value>
                    <value>update</value>
                    <value>delete</value>
                    <value>insert</value>
                </list>
            </entry>
        </map>
    </property>
</bean>

二、代码实现

1、数据源handle

在后面的aspect切面方法里,可以看到会给当前执行方法选择一个数据源的名称,把该名称put到执行该方法的线程的ThreadMap(通过holder),然后在继承AbstractRoutingDataSource的类中determineCurrentLookupKey方法调用holder的get方法返回当前线程选择的数据源名称。

/**
 * 数据源的Handler类
 */
public class DataSourceHandler {

    // 数据源名称holder
    public static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>();

    /**
     * 将读、写数据源加到dataSourceHolder中
     */
    public static void putDataSource(String datasource) {
        dataSourceHolder.set(datasource);
    }

    /**
     * 从holer中获取数据源字符串
     */
    public static String getDataSource() {
        return dataSourceHolder.get();
    }
     /** 
     * @Description: 清除数据源类型 
     * @param  
     * @return void 
     */  
    public static void clearDataSourceType() {  
        dataSourceHolder.remove();  
    }  
}

2、继承AbstractRoutingDataSource实现自定义数据源ChooseDataSource

在spring配置文件中会注入map,该Map存储对应方法包含某些字符映射某个数据源,在切面方法中根据该Map给执行方法的线程选择数据源。

/**
 * 获取数据源,用于动态切换数据源
 */
public class ChooseDataSource extends AbstractRoutingDataSource {

    public static Map<String, List<String>> map = new HashMap<String, List<String>>();

    /**
     * 实现父类中的抽象方法,获取数据源名称
     * @return
     */
    protected Object determineCurrentLookupKey() {
        return DataSourceHandler.getDataSource();
    }


}

3、切面方法根据DAO类执行方法名称选择数据源

PS:很多人可能会使用失败,原因很可能就是下面的Asprct配置的问题

/**
 * 切换数据源(不同方法调用不同数据源)
 */
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect {


    @Pointcut("execution(*.*.*(..))")
    public void aspect() {
    }

    /**
     * 配置前置通知,使用在方法aspect()上注册的切入点
     */
     /**
     *该方法主要判断执行的Dao类方法名是否包含指定字符子**串,从而选择对应设定的数据源名。
     */
    @Before("aspect()")
    public void before(JoinPoint point) {
        String className = point.getTarget().getClass().getName();
        String method = point.getSignature().getName();
        try {
            for (String key : ChooseDataSource.map.keySet()) {
                for (String type : ChooseDataSource.map.get(key)) {
                    if (method.startsWith(type)) {
                        DataSourceHandler.putDataSource(key);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

好了,到这里就大功告成了,体验体验效果吧。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值