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

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

SSM框架——动态路由+AOP实现多数据源配置_第1张图片
首先引入两个与aspectj相关的依赖,用来支持切面编程的

  • aspectjrt包是aspectj的runtime包
  • aspectjweaver是aspectj的织入包
  • Cglib包是用来动态代理用的,基于类的代理(此处使用jdk动态代理)

<dependency>
     <groupId>aspectjgroupId>
     <artifactId>aspectjrtartifactId>
     <version>1.5.3version>
dependency>
<dependency>
     <groupId>org.aspectjgroupId>
     <artifactId>aspectjweaverartifactId>
     <version>1.6.11version>
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配置文件中配置切面:


    <bean id="dataSourceAspect" class="com.myfund.saveplan.config.DataSourceAspect"/>
    <aop:config>
        
        
        <aop:pointcut id="dataSourcePointcut" expression="execution(* com.myfund.saveplan.service..*.*(..))"/>

        
        
        <aop:aspect ref="dataSourceAspect">
            
            <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 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}"/>
    
    
    
    <property name="initialSize" value="${jdbc.initialSize}"/>
    
    <property name="minIdle" value="${jdbc.minIdle}"/>
    
    <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}"/>
    
    
    
    <property name="initialSize" value="${jdbc.initialSize}"/>
    
    <property name="minIdle" value="${jdbc.minIdle}"/>
    
    <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="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);
}

你可能感兴趣的:(SSM)