spring动态设置多数据源--AbstractRoutingDataSource

版权声明:转载请注明原链接 https://blog.csdn.net/lij231/article/details/82934588

spring 有个类叫AbstractRoutingDataSource,只需要继承并重写方法就行了。

首先要先定义几个数据源

 <bean id="master" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${master.jdbc.driverClassName}"/>
        <property name="url" value="${master.jdbc.url}"/>
        <property name="username" value="${master.jdbc.username}"/>
        <property name="password" value="${master.jdbc.password}"/>
        <property name="filters" value="stat,wall"/>
    </bean>

    <bean id="assistant" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${log.jdbc.driverClassName}"/>
        <property name="url" value="${log.jdbc.url}"/>
        <property name="username" value="${log.jdbc.username}"/>
        <property name="password" value="${log.jdbc.password}"/>
    </bean>

定义一个注解叫RoutingDataSource

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RoutingDataSource {
    DataSources value() default DataSources.MASTER;
}

RoutingDataSource里只有一个属性 value是个枚举类

public enum DataSources {
    MASTER("master"),
    LOG("assistant");

    private String key;

    DataSources(String key) {
        this.key = key;
    }

    public String key() {
        return key;
    }
}

然后再定义一个与线程绑定的动态数据源

public class DataSourceKeyHolder {

    private static final ThreadLocal<LinkedList<String>> holder = new ThreadLocal<LinkedList<String>>() {
        @Override
        protected LinkedList<String> initialValue() {
            return new LinkedList<>();
        }
    };

    public static void set(String key) {
        holder.get().push(key);
    }

    public static void clear() {
        holder.get().pop();
    }

    public static String getCurrentKey() {
        if (holder.get().size() == 0)
            return null;
        return holder.get().getFirst();
    }

    public static boolean isNestedCall() {
        return holder.get().size() > 1;
    }

}

定义了一些基本方法,使用的是栈,这样就能满足嵌套调用,取的话只取第一个。再定义一个DynamicDataSource 继承于AbstractRoutingDataSource,并重写其中的determineCurrentLookupKey方法

public class DynamicDataSource extends AbstractRoutingDataSource {

    protected Object determineCurrentLookupKey() {
        return DataSourceKeyHolder.getCurrentKey();
    }

    @Override
    public Connection getConnection() throws SQLException {
        Connection connection = super.getConnection();
        if (DataSourceKeyHolder.getCurrentKey() != null) {
            log.info("Datasource route to {}, key={}", connection, DataSourceKeyHolder.getCurrentKey());
        }
        return connection;
    }
}

最后 ,则是具体的设置数据源的方法,在这使用AOP切入

@Component
@Aspect
public class RoutingDataSourceAdvisor {

    @Pointcut("execution(@RoutingDataSource * com.ljh..*Service+.*(..))")
    private void routingDataSource() {
    }

    @Around("routingDataSource()")
    public Object routing(ProceedingJoinPoint joinPoint) throws Exception {

        Class<?> clazz = joinPoint.getTarget().getClass();
        String className = clazz.getName();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String methodName = method.getName();
        Object[] arguments = joinPoint.getArgs();

        String key;
        RoutingDataSource routingDataSource = method.getAnnotation(RoutingDataSource.class);
        key = routingDataSource.value().getKey();

        Object result = null;
        DataSourceKeyHolder.set(key);

        try {
            checkPROPAGATION(clazz, method);
            result = joinPoint.proceed(arguments);
        } catch (Throwable e) {
            log.error("Error occurred during datasource(key=" + key + ") routing, ", e);
        } finally {
            DataSourceKeyHolder.clear();
        }
        return result;
    }

    private void checkPROPAGATION(Class<?> clazz, Method method) {
        if (DataSourceKeyHolder.isNestedCall()) {
            Transactional transactional = method.getAnnotation(Transactional.class);
            if (transactional == null) {
                transactional = clazz.getAnnotation(Transactional.class);
            }
            if (transactional != null) {
                if (transactional.propagation() != Propagation.REQUIRES_NEW) {
                    throw new RuntimeException("必须定义为propagation = REQUIRES_NEW");
                }
            }
        }
    }

}

在这里,要注意判断事务的传播级别是否是PROPAGATION_REQUIRED   ,如果不是Propagation.REQUIRES_NEW 的话,那数据源就是你的第一个数据源,它会和线程绑定,接下来取的话就不是调用我们定义的数据源了,而是从spring的线程变量里直接取了,所以这里要给个判断。

猜你喜欢

转载自blog.csdn.net/lij231/article/details/82934588
今日推荐