• AOP获取方法注解实现动态切换数据源


    AOP获取方法注解实现动态切换数据源(以下方式尚未经过测试,仅提供思路)

    ------

    自定义一个用于切换数据源的注解:

    package com.xxx.annotation;
    
    import org.springframework.stereotype.Component;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Component
    public @interface DataSource {
        String value() default "";
    
    }

    定义一个工具类,方便设置、删除、获取从数据源注解中得到的不同数据源类型:

    package com.xxx.utils.dataSource;
    
    public class DataSourceHolder {
    
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    
        /**
         * @Description: 设置数据源类型
         * @param dataSourceType 数据库类型
         * @return void
         * @throws
         */
        public static void setDataSourceType(String dataSourceType) {
            contextHolder.set(dataSourceType);
        }
    
        /**
         * @Description: 获取数据源类型
         * @param
         * @return String
         * @throws
         */
        public static String getDataSourceType() {
            return contextHolder.get();
        }
    
        /**
         * @Description: 清除数据源类型
         * @param
         * @return void
         * @throws
         */
        public static void clearDataSourceType() {
            contextHolder.remove();
        }
    }

    --------

    配置动态数据源:

    spring配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
        <!-- 引入配置文件 -->
        <bean id="propertyConfigurer"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="classpath:jdbc.properties" />
        </bean>
    
        <bean id="dataSource_R" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name ="jndiName">
            <!-- 下面第一行为测试环境配置,第二行为生产环境配置,运行时保留一个 -->
                <value>java:comp/env/jdbc/JTORDER</value>
                <!-- <value>jdbc/JTORDER</value> -->
            </property>
        </bean>
    
        <bean id="dataSource_RW" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name ="jndiName">
             <!-- 下面第一行为测试环境配置,第二行为生产环境配置,运行时保留一个 -->
                <value>java:comp/env/jdbc/JTORDER</value>
               <!--  <value>jdbc/JTORDER</value> -->
            </property>
        </bean>
    
        <!-- 动态数据源 -->
        <bean id="dataSource" class="com.xxx.utils.dataSource.RoutingDataSource">
            <!-- 为targetDataSources注入两个数据源 -->
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <entry key="R" value-ref="dataSource_R"/>
                    <entry key="RW" value-ref="dataSource_RW"/>
                </map>
            </property>
            <!-- 为指定数据源RoutingDataSource注入默认的数据源-->
            <property name="defaultTargetDataSource" ref="dataSource_R"/>
        </bean>
    
            <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <!-- 自动扫描mapping.xml文件 -->
            <!--<property name="mapperLocations" value="classpath:mapping/*.xml"></property>-->
            <property name="configLocation" value="classpath:mybatis-config.xml" />
            <property name="mapperLocations" value="classpath*:module.*.mapper/*.xml"></property>
        </bean>
    
        <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
        <bean id="transactionManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- xml接口映射文件 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.xxx.mapper"></property>
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        </bean>
    
        <!-- 配置事物切点 -->
        <aop:config>
            <aop:pointcut id="transactionPointcut" expression="execution(* com.xxx.service.impl.*.*(..) )"/>
            <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
        </aop:config>
    
        <!-- 注解方式配置事务-->
        <!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->
    
        <!-- 拦截器方式配置事务-->
        <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*" propagation="REQUIRED" />
            </tx:attributes>
        </tx:advice>
    
        <!-- 自动扫描定时任务 -->
        <task:annotation-driven/>
        <!-- spring自动创建代理,植入切面,proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy
        poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    </beans>
    
    
    
    <!--     
        本地测试需要在Tomcat的context.xml的 <Context> 标签中加入如下配置:
        <Resource
        name="jdbc/myName1"
        scope="Shareable"
        type="javax.sql.DataSource"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        url="jdbc:oracle:thin:@10.10.10.10:1521:xxdb1"
        driverClassName ="oracle.jdbc.driver.OracleDriver"
        username="usesr1"
        password="pwd1"
        />
    
        <Resource
        name="jdbc/myName2"
        scope="Shareable"
        type="javax.sql.DataSource"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        url="jdbc:oracle:thin:@10.10.10.10:1521:xxdb2"
        driverClassName ="oracle.jdbc.driver.OracleDriver"
        username="user2"
        password="pwd2"
        /> -->

    动态数据源类【其中RoutingDataSource 和上面xml中的RoutingDataSource 对应】:

    package com.xxx.utils.dataSource;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    
    public class RoutingDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceHolder.getDataSourceType();
        }
    }

    它的作用是通过获取我们自定义的数据源类型持有工具类DataSourceHolder中存储的数据源类型来动态试用真正的数据源

    --------

    通过AOP获取方法上的注解实现动态切换数据源

    (其中@Order(1)作用:

    Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟spring的事务aop执行的先后顺序问题,比如说动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了Order(排序)这个关键字.)

    import java.lang.reflect.Method;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Repository;


    @Order(1) @Aspect @Repository public class DataSourceAspect { @Pointcut("execution(* com.xxx.service.impl.*.*(..))") private void anyMethod() {} @AfterReturning(value = "anyMethod()", returning = "result") public void afterReturning(JoinPoint joinPoint,Object result){ DataSourceHolder.clearDataSourceType(); } @Before(value="anyMethod()") public void before(JoinPoint joinPoint){
         //通过切点对象获取当前切点所在的方法对象 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); //如果方法体上使用了DataSource注解 if (method.isAnnotationPresent(DataSource.class)) { //获取该方法上的注解名 DataSource datasource = method.getAnnotation(DataSource.class); //将方法体上的注解的值赋予给DataSourceHolder数据源持有类 DataSourceHolder.setDataSourceType(datasource.value()); } } }

    试用时只需要在被切方法上加上自定义注解,并且在里面配上不同的目标数据源即可。

  • 相关阅读:
    获得指定目录路径
    播放音乐(mciSendString)
    INotifyPropertyChanged接口
    从excel表格加载数据返回DataSet
    事件与委托
    .net中实现伪静态的学习小结
    今天开通博客了
    EasyUI后台管理系统学习四
    EasyUI后台管理系统学习三
    EasyUI后台管理系统学习二
  • 原文地址:https://www.cnblogs.com/libin6505/p/11227267.html
Copyright © 2020-2023  润新知