• Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用


    【参考】Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

     【参考】 利用Spring的AbstractRoutingDataSource解决多数据源的问题

    一:关于具体的原理说明请卡上面的参考链接

    二:操作步骤 :在你数据库配置文件中(我的是spring-dao.xml)配置多数据源  这里只展示了 数据库的配置

     

    <!-- 配置整合mybatis-->
        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath*:jdbc.properties</value>
                </list>
            </property>
        </bean>
    
        <!--配置数据库的连接池-->
        <bean id="abstractDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <!-- 配置链接属性 多数据源配置-->
           <!-- <property name="driverClass" value="${driver}"/>
            <property name="jdbcUrl" value="${url}"/>
            <property name="user" value="${username}"/>
            <property name="password" value="${password}"/>-->
            <!--配置c3p0连接池的私有属性-->
            <property name="maxPoolSize" value="30"/>
            <property name="minPoolSize" value="10"/>
            <!--关闭链接后不自动commit-->
            <property name="autoCommitOnClose" value="false"/>
            <property name="checkoutTimeout" value="1000"/>
            <!--获取连接失败重试次数-->
            <property name="acquireRetryAttempts" value="2"/>
        </bean>
        <!--多数据源配置 parent指向上面的配置数据库的连接池abstractDataSource 这里配置了两个数据源-->
        <bean id="base" parent="abstractDataSource">
            <property name="driverClass" value="${driver}"/>
            <property name="jdbcUrl" value="${url}"/>
            <property name="user" value="${username}"/>
            <property name="password" value="${password}"/>
        </bean>
        <bean id="server_1" parent="abstractDataSource">
            <property name="driverClass" value="${server_1_driver}"/>
            <property name="jdbcUrl" value="${server_1_url}"/>
            <property name="user" value="${server_1_username}"/>
            <property name="password" value="${server_1_password}"/>
        </bean>
    
        <!-- 在RoutingDataSource类中读取 当前的返回值 并匹配key值 选择你的数据库源-->
        <bean id="dataSource" class="com.cdms.dao.resources.RoutingDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <entry key="base" value-ref="base"></entry>
                    <entry key="server_1" value-ref="server_1"></entry>
                </map>
            </property>
            <property name="defaultTargetDataSource" ref="base"></property>
        </bean>
    
        <!-- 配置sqlSessionFactory对象-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 注入数据库连接池-->
            <property name="dataSource" ref="dataSource"/>
            <!-- 配置Mybatisq全局文件:-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
    
            <!-- 扫描entity包-->
            <property name="typeAliasesPackage" value="com.cdms.entity"/>
            <!-- 扫描sql配置文件-->
            <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        </bean>

    另外 我的jdbc:perperties 配置文件是

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/course_design?useUnicode=true&characterEncoding=UTF-8
    username=root
    password=root
    
    server_1_driver=com.mysql.jdbc.Driver
    server_1_url=jdbc:mysql://127.0.0.1:3306/course_design_test?useUnicode=true&characterEncoding=UTF-8
    server_1_username=root
    server_1_password=root
    RoutingDataSource类和DynamicDataSourceHolder类 
    public class RoutingDataSource extends AbstractRoutingDataSource{
        public final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Override
        protected Object determineCurrentLookupKey() {
            String dataSourceName = DynamicDataSourceHolder.getDataSourceName();
            if(dataSourceName == null){
                dataSourceName = DataSourceType.BASE.getDataSource();
            }
            logger.info("选择的数据库是:"+dataSourceName);
            return dataSourceName;
        }
    }
    public class DynamicDataSourceHolder {
    
        //解决线程安全问题
        private static final ThreadLocal<String> holder = new ThreadLocal<String>();
    
        public static void putDataSourceName(String dataName){
            holder.set(dataName);
        }
    
        public static String getDataSourceName(){
            return holder.get();
        }
    
        public static class DataSourceName{
            public final static String BASE = "base";
        }
    }

    我们可以通过在holder中 put数据库的key值 来实现数据源的动态加载,其中 ThreadLocal<String> holder 保证了线程安全,而 RoutingDataSource
    类里面的determineCurrentLookupKey()方法返回值 就是数据源的key值 配置文件通过这个key值找到了当前的配置源链接。这个是的测试 我就不多演示。

    那我们怎么动态的进行数据链接呢,我们可以通过aop切面变成来实现

    具体的aop切面编程的配置这里不多做介绍,可以参考之前的记过博文  有介绍
    三:AOP注入实现 数据源的动态配置  首先需要第一一个切点。

    package com.cdms.aop;
    
    import com.cdms.eunms.DataSourceType;
    
    import java.lang.annotation.*;
    
    /**
     * 创建 by 草帽boy on 2017/3/29.
     */
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ChooseDataSource {
        /**
         * 数据库名称 默认似base
         * @return 数据库名称
         */
        String dataSourceName() default "base";
    
    }

    然后定义一个切面方法

    package com.cdms.aop;
    
    import com.cdms.dao.resources.DynamicDataSourceHolder;
    import com.cdms.eunms.DataSourceType;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 创建 by 草帽boy on 2017/3/29.
     */
    @Aspect
    @Component
    public class GetDataSource {
    
        /**
         * 切面设置要选择的数据库
         * @param joinPoint
         * @return
         * @throws Throwable
         */
        @Around("@annotation(com.cdms.aop.ChooseDataSource)")
        public Object permission(ProceedingJoinPoint joinPoint) throws Throwable {
            Object target = joinPoint.getTarget();
            Object[] args = joinPoint.getArgs();
            Method method = getMethod(joinPoint, args);
            //获取数据库名称参数
            ChooseDataSource chooseDataSource = method.getAnnotation(ChooseDataSource.class);
            if(chooseDataSource != null){
                String dataSourceName = chooseDataSource.dataSourceName();
                //坚持名称是否合法 如果不合法 就用默认数据库
                if(DataSourceType.check(dataSourceName)){
                    DynamicDataSourceHolder.putDataSourceName(dataSourceName);
                }else {
                    DynamicDataSourceHolder.putDataSourceName(DataSourceType.BASE.getDataSource());
                }
            }
            return joinPoint.proceed();
        }
    
        /**
         * 获取切面方法
         * @param joinPoint
         * @param args
         * @return
         * @throws NoSuchMethodException
         */
        private Method getMethod(ProceedingJoinPoint joinPoint, Object[] args) throws NoSuchMethodException {
    //        Class[] argsClazz = new Class[args.length];
    //        for(int i = 0 ; i < args.length; i++){
    //            argsClazz[i] = args[i].getClass();
    //        }
            String methodName = joinPoint.getSignature().getName();
            Class clazz = joinPoint.getTarget().getClass();
            Method[] methods = clazz.getMethods();
            for(Method method : methods) {
                if (methodName.equals(method.getName())) {
                    return method;
                }
            }
            return null;
        }
    }
     <!-- 启动@aspectj的自动代理支持-->
        <aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
        <!-- 定义aspect类 -->
        <bean id="logAopAction" class="com.cdms.aop.LogAopAction"></bean>
        <bean id="aopTest" class="com.cdms.aop.DemoAopTest"></bean>
        <bean id="getDataSourcesName" class="com.cdms.aop.GetDataSource"></bean>

    在之后需要改变数据源的地方 只需要 @ChooseDataSource(dataSourceName = "server_1")   表明你的数据源就看了

    使用单元测试的结果是 ;(@ChooseDataSource(dataSourceName = "server_1") )

    (@ChooseDataSource(dataSourceName = "base") )

     

    这个是注入点:

  • 相关阅读:
    精《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #4 如何使用Git
    《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #3 如何编写内核模块
    《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #2 如何编译Linux内核
    《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #1 如何获取Linux内核
    [失败]SystemTap和火焰图(Flame Graph)
    yum安装nagois
    yum安装cacti
    笔记本制作centos qcow2格式文件
    【失败】CentOS 6.5安装VNCserver 并开启远程桌面
    Linux 性能分析的前 60 秒
  • 原文地址:https://www.cnblogs.com/luffyu/p/6641267.html
Copyright © 2020-2023  润新知