理解spring动态切换数据源,需要对spring具有一定的了解
工作中经常遇到读写分离,数据源切换的问题,那么以下是本作者实际工作中编写的代码 与大家分享一下!
1.定义注解 DataSource
package com.gomecar.index.datasource; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据源切换注解 * @author xiaotian * */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) public @interface DataSource { String value() default "read"; }
2. 定义切面DataSourceAspect
package com.gomecar.index.datasource; import java.lang.reflect.Method; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; /** * 用于数据库读写分离的切面 * @author xiaotian * */ @Aspect public class DataSourceAspect { //日志 private Logger logger = Logger.getLogger(this.getClass()); /** * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源 * * @param point * @throws Exception */ public void intercept(JoinPoint point) throws Exception { Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); for (Class<?> clazz : target.getInterfaces()) { resolveDataSource(clazz, signature.getMethod()); } resolveDataSource(target, signature.getMethod()); } /** * 提取目标对象方法注解和类型注解中的数据源标识 * * @param clazz * @param method */ private void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); // 默认使用类型注解dataSourcePointcutdataSourcePointcut if (clazz.isAnnotationPresent(DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); DynamicDataSourceHolder.putDataSource(source.value()); } // 方法注解可以覆盖类型注解 Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); DynamicDataSourceHolder.putDataSource(source.value()); } } catch (Exception e) { logger.error("切换数据源error", e); } } //通知 public void before(JoinPoint point) { Object target = point.getTarget(); String method = point.getSignature().getName(); //反射获取接口 Class<?>[] classz = target.getClass().getInterfaces(); //获取返回值类型 Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); try { //获取方法 Method m = classz[0].getMethod(method, parameterTypes); //根据方法上注解 获取只读数据库连接池 或者只写数据库连接池 if (m != null && m.isAnnotationPresent(DataSource.class)) { //根据注解值获取数据库连接池 DataSource data = m .getAnnotation(DataSource.class); DynamicDataSourceHolder.putDataSource(data.value()); System.out.println(data.value()); } } catch (Exception e) { // TODO: handle exception } } }
3.获取动态数据源DynamicDataSource
package com.gomecar.index.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态获取数据源 * @author xiaotian * */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceHolder.getDataSouce(); } }
4.数据源持有者DynamicDataSourceHolder
package com.gomecar.index.datasource; /** * 数据库连接池 持有者 * 将数据库连接绑定到当前线程 * @author xiaotian * */ public class DynamicDataSourceHolder { //绑定当前线程 public static final ThreadLocal<String> holder = new ThreadLocal<String>(); //设置数据源 public static void putDataSource(String name) { holder.set(name); } //获取数据源 public static String getDataSouce() { return holder.get(); } }