配置
<!-- 第一个数据源 -->
<bean id="dataSource01" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg>
<bean class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- <property name="autoCommit" value="true"/> -->
<property name="poolName" value="dataSource01"/>
<property name="connectionTimeout" value="${jdbc.connectionTimeout:30000}"/>
<property name="idleTimeout" value="${jdbc.idleTimeout:600000}"/>
<property name="maxLifetime" value="${jdbc.maxLifetime:1800000}"/>
<property name="maximumPoolSize" value="${jdbc.maximumPoolSize:100}"/>
<property name="minimumIdle" value="${jdbc.minimumIdle:10}"/>
<!-- <property name="metricRegistry" ref="metric"/> -->
<!-- <property name="healthCheckRegistry" ref="healthCheckRegistry"></property>-->
<property name="connectionTestQuery" value="SELECT 1"/>
</bean>
</constructor-arg>
</bean>
<!-- 第二个数据源 -->
<bean id="dataSource02" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg>
<bean class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- <property name="autoCommit" value="true"/> -->
<property name="poolName" value="risk"/>
<property name="connectionTimeout" value="${jdbc.connectionTimeout:30000}"/>
<property name="idleTimeout" value="${jdbc.idleTimeout:600000}"/>
<property name="maxLifetime" value="${jdbc.maxLifetime:1800000}"/>
<property name="maximumPoolSize" value="${jdbc.maximumPoolSize:100}"/>
<property name="minimumIdle" value="${jdbc.minimumIdle:10}"/>
<!-- <property name="metricRegistry" ref="metric"/> -->
<!-- <property name="healthCheckRegistry" ref="healthCheckRegistry"></property>-->
<property name="connectionTestQuery" value="SELECT 1"/>
</bean>
</constructor-arg>
</bean>
<!-- 自定义数据源导航类,根据key值返回对应的数据源 -->
<!-- 配置动态切换数据源 -->
<bean id="dataSource" class="com.xxxx.xxxx.xxxx.router.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource01" value-ref="dataSource01"></entry>
<entry key="dataSource02" value-ref="dataSource02"></entry>
</map>
</property>
<!-- 默认目标数据源为你主库数据源 -->
<property name="defaultTargetDataSource" ref="dataSource01"/>
</bean>
<!-- 配置切面 -->
<bean id="dataSourceAspect" class="com.xxxx.xxxx.xxxx.router.DataSourceAspect"/>
<!-- AOP动态切换数据源 -->
<aop:config>
<aop:aspect ref="dataSourceAspect" order="1">
<!-- 拦截对应的业务方法 -->
<aop:pointcut id="dataSourcePointcut" expression="execution(public * com.xxxx.xxxx.xxx.service.impl.*.*(..))"/>
<!-- 通过AOP对注解进行验证 -->
<aop:before pointcut-ref="dataSourcePointcut" method="intercept"/>
</aop:aspect>
</aop:config>
注意:此切面的优先级要高于事务的优先级
注解类
注解可作用于方法、接口、类上
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomDataSource {
String value();
String D01 = "dataSource01";
String D02 = "dataSource02";
}
切面类
public class DataSourceAspect {
/**
* 拦截目标方法,通过注解来获取数据源名称key值,设置到ThreadLocal中
*/
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());
}
/**
* 获取目标对象方法注解和类型注解中的注解
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
// 默认使用类型注解
if (clazz.isAnnotationPresent(CustomDataSource.class)) {
CustomDataSource cds = clazz.getAnnotation(CustomDataSource.class);
BindingDataSourceKey.setDataSourceToken(cds.value());
}
// 方法注解覆盖,以方法注解为最后值
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(CustomDataSource.class)) {
CustomDataSource cds = m.getAnnotation(CustomDataSource.class);
BindingDataSourceKey.setDataSourceToken(cds.value());
}
} catch (Exception e) {
log.error(clazz + ":" + e.getMessage());
}
}
}
ThreadLocal 类
public class BindingDataSourceKey {
private static final ThreadLocal<String> THREAD_LOCAL_TOKEN = new ThreadLocal<String>();
public static String getDataSourceToken() {
return THREAD_LOCAL_TOKEN.get();
}
public static void setDataSourceToken(String dataSource) {
THREAD_LOCAL_TOKEN.set(dataSource);
}
public static void clearDataSourceToken() {
THREAD_LOCAL_TOKEN.remove();
}
}
获取数据源对应key的类
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
String dataSource = BindingDataSourceKey.getDataSourceToken();
// 获取完后解除绑定,防止其他的service方法调用该数据源
BindingDataSourceKey.clearDataSourceToken();
return dataSource;
}
}
之后需要在注解的类上、方法或是接口上加上注解@CustomDataSource(CustomDataSource.D02),就可以通过切入点表达式进行获取注解情况,从而控制数据源
网友评论