• spring 多数据源动态切换


    理解spring动态切换数据源,需要对spring具有一定的了解

    工作中经常遇到读写分离,数据源切换的问题,那么以下是本作者实际工作中编写的代码  与大家分享一下!

      

    1.定义注解 DataSource 

    package com.gomecar.index.datasource;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 数据源切换注解
     * @author xiaotian
     *
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE,ElementType.METHOD})
    public @interface DataSource {
    
        String value() default "read";
    
    }

    2. 定义切面DataSourceAspect

    package com.gomecar.index.datasource;
    
    import java.lang.reflect.Method;
    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    
    /**
     * 用于数据库读写分离的切面
     * @author xiaotian
     *
     */
    @Aspect
    public class DataSourceAspect {
    
        //日志
        private Logger logger = Logger.getLogger(this.getClass());
    
        /**
         * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
         *
         * @param point
         * @throws Exception
         */
        public void intercept(JoinPoint point) throws Exception {
            Class<?> target = point.getTarget().getClass();
            MethodSignature signature = (MethodSignature) point.getSignature();
            for (Class<?> clazz : target.getInterfaces()) {
                resolveDataSource(clazz, signature.getMethod());
            }
            resolveDataSource(target, signature.getMethod());
        }
    
        /**
         * 提取目标对象方法注解和类型注解中的数据源标识
         *
         * @param clazz
         * @param method
         */
        private void resolveDataSource(Class<?> clazz, Method method) {
            try {
                Class<?>[] types = method.getParameterTypes();
                // 默认使用类型注解dataSourcePointcutdataSourcePointcut
                if (clazz.isAnnotationPresent(DataSource.class)) {
                    DataSource source = clazz.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.putDataSource(source.value());
                }
                // 方法注解可以覆盖类型注解
                Method m = clazz.getMethod(method.getName(), types);
                if (m != null && m.isAnnotationPresent(DataSource.class)) {
                    DataSource source = m.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.putDataSource(source.value());
                }
            } catch (Exception e) {
                logger.error("切换数据源error", e);
            }
        }
    
    
    
        //通知
        public void before(JoinPoint point)
        {
            Object target = point.getTarget();
            String method = point.getSignature().getName();
            //反射获取接口
            Class<?>[] classz = target.getClass().getInterfaces();
            //获取返回值类型 
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                    .getMethod().getParameterTypes();
            try {
                //获取方法
                Method m = classz[0].getMethod(method, parameterTypes);
                //根据方法上注解  获取只读数据库连接池 或者只写数据库连接池
                if (m != null && m.isAnnotationPresent(DataSource.class)) {
                    //根据注解值获取数据库连接池
                    DataSource data = m
                            .getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.putDataSource(data.value());
                    System.out.println(data.value());
                }
                
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }

    3.获取动态数据源DynamicDataSource

    package com.gomecar.index.datasource;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    /**
     * 动态获取数据源
     * @author xiaotian
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceHolder.getDataSouce();
        }
    
    }

    4.数据源持有者DynamicDataSourceHolder

    package com.gomecar.index.datasource;
    /**
     * 数据库连接池 持有者
     * 将数据库连接绑定到当前线程
     * @author xiaotian
     *
     */
    public class DynamicDataSourceHolder {
        //绑定当前线程
        public static final ThreadLocal<String> holder = new ThreadLocal<String>();
        //设置数据源
        public static void putDataSource(String name) {
            holder.set(name);
        }
        //获取数据源
        public static String getDataSouce() {
            return holder.get();
        }
    }
  • 相关阅读:
    Windows 右键添加用记事本打开的选项
    shell 脚本常用写法
    常用命令--dig
    电子表格数字式的小时化成时分秒格式
    Kaggle猫狗图像分类竞赛Baseline
    阿里巴巴用户体验研究专员暑期实习生笔试 经验分享 2019
    sysctl -w net.core.somaxconn=65535
    src/stream/ngx_stream_proxy_module.c:542: 错误:‘ngx_stream_upstream_t’没有名为‘ssl_name’的成员
    ssl.cpp:333: error: ‘SSL_set_tlsext_host_name’ was not declared in this scope
    fiddler QuickExec
  • 原文地址:https://www.cnblogs.com/shoutn/p/7800916.html
Copyright © 2020-2023  润新知