您的位置:首页 > 编程语言 > Java开发

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

2017-07-12 11:30 531 查看
一般情况下,很多项目(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();
}
}
}


好了,到这里就大功告成了,体验体验效果吧。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息