SSM框架——动态路由+AOP实现多数据源配置

一、数据源切换及选择处理工具类引入

在这里插入图片描述
首先引入两个与aspectj相关的依赖,用来支持切面编程的

  • aspectjrt包是aspectj的runtime包
  • aspectjweaver是aspectj的织入包
  • Cglib包是用来动态代理用的,基于类的代理(此处使用jdk动态代理)
<!-- aop使用 -->
<dependency>
     <groupId>aspectj</groupId>
     <artifactId>aspectjrt</artifactId>
     <version>1.5.3</version>
</dependency>
<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.6.11</version>
</dependency>
<!--<dependency>-->
     <!--<groupId>cglib</groupId>-->
     <!--<artifactId>cglib</artifactId>-->
     <!--<version>2.1</version>-->
<!--</dependency>-->
  1. 创建注解类(后面设置数据源时需要使用该注解)
package com.myfund.saveplan.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//该类型的注解会被保留到那个阶段
@Retention(RetentionPolicy.RUNTIME)
//注解执行的目标地址  TYPE  类/接口    METHOD  方法    PARAMETER  参数
@Target(ElementType.METHOD)
public @interface DataSource {
    String value();
}
  1. 创建数据源切面类(获取当前线程数据源路由的key值)
package com.myfund.saveplan.config;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;


public class DataSourceAspect {
    /**
     * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
     */
    public void intercept(JoinPoint point) {
        Class<?> target = point.getTarget().getClass();
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 默认使用目标类型的注解,如果没有则使用其实现接口的注解
        for (Class<?> clazz : target.getInterfaces()) {
            resolveDataSource(clazz, signature.getMethod());
        }
        resolveDataSource(target, signature.getMethod());
    }

    /**
     * 提取目标对象方法注解和类型注解中的数据源标识
     *
     * @author: haocheng
     * @date: 2019-05-21 11:43
     */
    private void resolveDataSource(Class<?> clazz, Method method) {
        try {
            Class<?>[] types = method.getParameterTypes();
            //默认使用类型注解
            if (clazz.isAnnotationPresent(DataSource.class)) {
                DataSource ds = clazz.getAnnotation(DataSource.class);
                HandleDataSource.setDataSource(ds.value());
            }
            // 方法注解可以覆盖类型注解
            Method m = clazz.getMethod(method.getName(), types);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource source = m.getAnnotation(DataSource.class);
                HandleDataSource.setDataSource(source.value());
            }
        } catch (Exception e) {
            System.out.println(clazz + ":" + e.getMessage());
        }
    }

}

同时在springmv-mybatis配置文件中配置切面:

<!-- 通过AOP来捕获并实现@DataSource注解 -->
    <bean id="dataSourceAspect" class="com.myfund.saveplan.config.DataSourceAspect"/>
    <aop:config>
        <!-- where:在哪些地方(包.类.方法)做增加 -->
        <!-- 拦截所有service方法 -->
        <aop:pointcut id="dataSourcePointcut" expression="execution(* com.myfund.saveplan.service..*.*(..))"/>

        <!-- 封装成下面的一个通知Advice -->
        <!-- what:做什么增强 -->
        <aop:aspect ref="dataSourceAspect">
            <!-- when:在什么时机(方法前/后/前后) -->
            <aop:before pointcut-ref="dataSourcePointcut" method="intercept"/>
        </aop:aspect>
    </aop:config>
  1. 创建数据源处理类

    线程共享工具,将数据源信息保存在ThreadLocal中实现共享,保证数据源信息在同一线程下切换后不被其他线程修改


public class HandleDataSource {
    public static final ThreadLocal<String> holder = new ThreadLocal<String>();

    /**
     * 绑定当前线程数据源路由
     *
     * @param datasource
     */
    public static void setDataSource(String datasource) {
        holder.set(datasource);
    }

    /**
     * 获取当前线程的数据源路由
     *
     * @return
     */
    public static String getDataSource() {
        return holder.get();
    }

}
  1. 创建数据源选择类

    spring为我们提供了AbstractRoutingDataSource,即带路由的数据源。
    继承后我们需要实现它的determineCurrentLookupKey(),该方法用于自定义实际数据源名称的路由选择方法
    由于我们将信息保存到了ThreadLocal中,所以只需要从中拿出来即可。

package com.myfund.saveplan.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;



public class ChooseDataSource extends AbstractRoutingDataSource {

    /**
     * 获取与数据源相关的key
     * 此key是Map<String, DataSource> resolvedDataSources 中与数据源绑定的key值
     * 在通过determineTargetDataSource获取目标数据源时使用
     */
    @Override
    protected Object determineCurrentLookupKey() {
        // 从共享线程中获取数据源名称
        return HandleDataSource.getDataSource();
    }
}

通过以上四个类的创建,实现了整个数据源配置和切换过程的底层处理配置,接下来就是框架中的一些基本配置。

二、项目开发所需数据源配置
开发使用的是SSM框架,所以在jdbc.properties配置文件中配置相关数据库连接信息

#sqlserver
sqlserver.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
sqlserver.url=jdbc:sqlserver://10.20.34.122;DatabaseName=PrimaryData_New
sqlserver.username=
sqlserver.password=


#oracle
oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@10.20.30.9:1521:racdb1
oracle.username=
oracle.password=



#初始化连接大小
jdbc.initialSize=10
#连接池最小空闲
jdbc.minIdle=1
#连接池最大数量
jdbc.maxActive=20
#获取连接最大等待时间
jdbc.maxWait=60000
#验证语句
jdbc.validationQuery=SELECT 1

三、修改spring-mybatis配置文件,对多个数据源进行配置(关键配置)

主要分为两方面:

  • 数据源信息基础配置
<!-- 第一个数据源 -->
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
	<property name="driverClassName" value="${sqlserver.driverClassName}"/>
    <property name="url" value="${sqlserver.url}"/>
    <property name="username" value="${sqlserver.username}"/>
    <property name="password" value="${sqlserver.password}"/>
    <!-- 配置初始化大小、最小、最大 -->
    <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
    <!-- 初始化时建立物理连接的个数,缺省值为0 -->
    <property name="initialSize" value="${jdbc.initialSize}"/>
    <!-- 最小连接池数量 -->
    <property name="minIdle" value="${jdbc.minIdle}"/>
    <!-- 最大连接池数量,缺省值为8 -->
    <property name="maxActive" value="${jdbc.maxActive}"/>
</bean>

<!-- 第二个数据源 -->
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${oracle.driverClassName}"/>
    <property name="url" value="${oracle.url}"/>
    <property name="username" value="${oracle.username}"/>
    <property name="password" value="${oracle.password}"/>
    <!-- 配置初始化大小、最小、最大 -->
    <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
    <!-- 初始化时建立物理连接的个数,缺省值为0 -->
    <property name="initialSize" value="${jdbc.initialSize}"/>
    <!-- 最小连接池数量 -->
    <property name="minIdle" value="${jdbc.minIdle}"/>
    <!-- 最大连接池数量,缺省值为8 -->
    <property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
  • 数据源路由配置
<!-- 数据源路由配置 -->
<bean id="multipleDataSource" class="com.myfund.saveplan.config.ChooseDataSource">
	<!-- 默认数据源 -->
    <property name="defaultTargetDataSource" ref="dataSource1"/>
    <!-- 目标数据源-->
    <property name="targetDataSources">
       <map>
            <entry key="dataSource1" value-ref="dataSource1"/>
            <entry key="dataSource2" value-ref="dataSource2"/>
       </map>
    </property>
</bean>

同时重新配置事务管理

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--<property name="dataSource" ref="dataSource"/>-->
    <property name="dataSource" ref="multipleDataSource"/>
</bean>
<!-- 配置基于注解的声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

注: 在spring-mybatis中进行数据源基础信息和数据源路由配置时,当增加新的数据源时,都需要为该数据源配置一个新的bean,同时在数据源路由中注入该bean

四、使用多数据源

在service层接口,通过数据源注解为每一个方法执行的数据源进行具体的配置,数据源注解中的值一定要和相应数据源配置文件中的bean的id值对应

package com.myfund.saveplan.service;
import com.myfund.saveplan.config.DataSource;
import java.util.List;

public interface RankService {
    List getRankInfo();
    
    @DataSource(value = "dataSource2")
    List getFeerate();
    
    List getYearProfit(String fundcode);
    
    List getMonthLast1DayUnitEquity(String fundcode);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值