随着业务的发展,用户量的增大,单表单库已经知足不了咱们的业务需求,所以须要分库分表,可是随之带来的问题是当咱们须要从库中捞取某些数据的时候,常常须要在多个数据库来回切换,如何作到无缝切换,这里主要经过配置数据源,自定义注解和aop技术来解决。主要包含如下4步:java
一、切换数据源spring
例如咱们有两个数据源,一个是ds0,一个是ds1,咱们能够经过以下方式配置一个动态数据源。咱们只须要自定义一个类继承AbstractRoutingDatasource,实现determineCurrentLookupKey方法,这个方法能够决定咱们每次使用的数据源。contextHolder则是一个线程局部变量,保证线程的安全。也就是只要每次在准备切换数据库的时候,经过contextHolder.setDatabaseName就能够切换数据源了。数据库
/** * 功能:数据库切换工具类 * 版本:1.0 * 日期:2016/12/2 13:44 * 做者:屠苏 */ public class DataSourceContextHolder { // 线程局部变量,用于保存保存数据库的名称 private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<String>(); public static void setDataBaseName(String dataBaseName) { contextHolder.set(dataBaseName); } public static String getDataBaseName() { return contextHolder.get(); } public static void clearDataBaseName() { contextHolder.remove(); } }
/** * 功能:动态数据源 * 版本:1.0 * 日期:2016/12/2 13:49 * 做者:屠苏 */ public class DynamicDataSource extends AbstractRoutingDataSource { // 返回数据源的key @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataBaseName(); } }
<!--配置动态数据源--> <bean id="dataSource" class="com.netease.payment.util.datasource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="ds0" value-ref="dataSource0"/> <entry key="ds1" value-ref="dataSource1"/> </map> </property> <property name="defaultTargetDataSource" ref="dataSource0"/> </bean>
二、自定义注解安全
首先作一个自定义注解,而后每次在须要切数据源的方法上加上@Datasource("xx")就能够了。ide
/** * 描述: 自定义数据源注解 * 版本号: 1.0 * 日期: 2017/2/21 11:49 * 做者: 屠苏 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Datasource { String value() default "ds0"; }
三、Aop工具
首先定义一个切入点,对全部存在@Datasource的注释的方法进行切面,而后获取签名方法,获取方法上的注释,替换数据源。spa
package com.elin4it.ssm.aop; import com.elin4it.ssm.annotation.Datasource; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 功能:切换数据源aop * 版本:1.0 * 日期:2016/12/29 14:18 * 做者:屠苏 */ @Aspect @Component public class DsAop { /** * 第一个 * 表明全部的返回值类型 * 第二个 * 表明全部的类 * 第三个 * 表明类全部方法 * 最后一个 .. 表明全部的参数 */ @Pointcut("@annotation(com.elin4it.ssm.annotation.Datasource)") private void anyMethod(){}//定义一个切入点 @Before("anyMethod()") public void before(JoinPoint joinPoint){ Object target = joinPoint.getTarget(); Class<?> classObj = target.getClass(); // 获取链接点的签名方法对象 Signature signature = joinPoint.getSignature(); System.out.println("signature method=" + signature.getName()); // 方法名 String methodName = joinPoint.getSignature().getName(); // 方法参数类型 Class<?>[] paraClasses = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes(); try { Method method = classObj.getMethod(methodName, paraClasses); if(method != null && method.isAnnotationPresent(Datasource.class)) { Datasource dataSource = method.getAnnotation(Datasource.class); /** * 切换数据源 !!!!!!! */ DataSourceContextHolder.setDataBaseName(dataSource.value()); System.out.println("value=" + dataSource.value()); } } catch (Exception e) { e.printStackTrace(); } } @After("anyMethod()") public void after(){} @AfterThrowing("anyMethod()") public void exception(){} @Around("anyMethod()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { Object object = pjp.proceed();//执行该方法 return object; } }
四、调用切换数据源的方法.net
@Datasource("ds1") public void testSwitchDS() { System.out.println("调用testSwitchDS方法!"); }