AbstractRoutingDataSource+AOP+JNDI实现spring动态数据源

参考:https://www.cnblogs.com/wyb628/p/7240061.html

  • 背景:

系统已有数据源1(主要数据源),数据源2(只有一个目录的xml使用该数据源),由于这2个数据源分别扫描不同的包,相互不打扰,所以一直用的好好的。

直到,需要新增一个数据源3,跟数据源2用法一模一样的,但是需要在程序中具体用到的时候才能决定具体使用哪一个。所以,基于此,针对数据源2和3实现了动态数据源。

  • 思路:
  1. sessionFactory的dataSource属性设置成能从代码中动态读取,继承类:org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
  2. 使用ThreadLocal为每一个线程单独设置数据源的key,该key可以匹配到配置文件中的jndi。
  3. 通过aop进行动态的设置key,使用完毕remove,防止出现内存泄露。
  • 代码如下:

spring-mybatis.xml配置数据源1和动态数据源2/3如下:<?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:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee-4.3.xsd">

    <!-- data source 1-->
    <jee:jndi-lookup id="dataSource" lookup-on-startup="false"
        proxy-interface="javax.sql.DataSource" jndi-name="${first.jndi.database}" />

    <!--  data source 2-->
    <jee:jndi-lookup id="secondDataSource"
        lookup-on-startup="false" proxy-interface="javax.sql.DataSource"
        jndi-name="${second.jndi.database}" />
    <!-- data source 3-->
    <jee:jndi-lookup id="thirdDataSource"
    lookup-on-startup="false" proxy-interface="javax.sql.DataSource"
    jndi-name="${third.jndi.database}"/>

    <bean id="PaginationInterceptor" class="XXXX.mybatis.plugins.PaginationInterceptor"/>

    <!-- 数据源1的配置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations">
        <array>
            <value>classpath:XXXXX/database/*/impl/*.xml</value>
            <value>classpath*:XXXX/client/dao/**/*Mapper.xml</value>
            <value>classpath*:XXXXX/restructure/**/*Mapper.xml</value>
        </array>
        </property>
        <property name="plugins">
            <array>
                <ref bean="PaginationInterceptor" />
                <ref bean="myInterceptor"/>
            </array>
        </property>
    </bean>

    <bean id="myInterceptor" class="XXXXX.mybatis.MyInterceptor"></bean>

    <!--配置数据源1的扫描路径-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="XXXX.data.database" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 注解方式配置事务 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- 动态数据源的配置-->
    <bean id="myDynamicDataSource" class="XXXX.DynamicDataSource">
    <property name="targetDataSources">
    <map key-type="java.lang.String">
    <!--注意这里key跟代码中对应即可 value要跟配置的jndi对应-->
    <entry value-ref="secondDataSource" key="mySecond"/>
    <entry value-ref="thirdDataSource" key="myThree"/>
    </map>
    </property>
    <!--配置默认的--->
    <property name="defaultTargetDataSource" ref="secondDataSource"/>
    </bean>

    <bean id="mySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDynamicDataSource" />
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations"
            value="classpath:YYYYY/*/impl/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="YYYYYXXXX.YYYdatabase" />
        <property name="sqlSessionFactoryBeanName" value="mySqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager_my"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDynamicDataSource" /> </bean> </beans>

其中:<bean id="myDynamicDataSource" class="XXXX.DynamicDataSource">

DynamicDataSource.java内容如下:

package XXXX;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getSourceType();
    }
}
DataSourceContextHolder.java内容如下:

package XXXX;


public class DataSourceContextHolder {
private static final ThreadLocal<String> contextDynamicSourceHolder = new ThreadLocal<String>();


public static void setDataSourceType(String sourceType) {
contextDynamicSourceHolder.set(sourceType);
}


public static String getSourceType() {
return contextDynamicSourceHolder.get();
}


public static void clearSourceType() {
contextDynamicSourceHolder.remove();
}
}

 

使用AOP对需要使用动态数据源的地方进行设置值,aop如下:

package XXXX;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class contextDynamicSourceHolder {
    private   final Logger LOG=LoggerFactory.getLogger(contextDynamicSourceHolder.class);

    //切点设置为需要使用动态数据源的地方
    @Pointcut("execution(* XXXX.ZZZZMapper.*(..))")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void before(JoinPoint jp) {
        Object[] args=jp.getArgs();
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] argNames = methodSignature.getParameterNames();
        //省略判断条件,根据判断条件设置数据源的key
        DataSourceContextHolder.setDataSourceType(XXX);
    }

    @AfterReturning("pointCut()")
    public void afterReturnning() {
        DataSourceContextHolder.clearSourceType();
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        DataSourceContextHolder.clearSourceType();
    }
}
 如果aop没有生效,1.检查一下spring.xml配置文件中是否有:<aop:aspectj-autoproxy/>2.检查切点表达式是否正确比如:execution(* XXX.UUUMapper.*(..))

XXX是路径  UUUMapper是具体的文件名称  .* 表示所有的方法 ()表示参数

原文地址:https://www.cnblogs.com/LittleSix/p/10816497.html

时间: 2024-10-09 03:09:12

AbstractRoutingDataSource+AOP+JNDI实现spring动态数据源的相关文章

Spring动态数据源实现读写分离

一.创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性 package com.bounter.mybatis.extension; /** * 基于ThreadLocal实现的动态数据源容器,保证DynamicDataSource的线程安全性 * @author simon * */ public class DynamicDataSourceHolder { private static final ThreadLocal<String> dataSourceHolde

spring 动态数据源

1.动态数据源:  在一个项目中,有时候需要用到多个数据库,比如读写分离,数据库的分布式存储等等,这时我们要在项目中配置多个数据库. 2.原理:   (1).spring 单数据源获取数据连接过程: DataSource --> SessionFactory --> Session  DataSouce   实现javax.sql.DateSource接口的数据源,  DataSource  注入SessionFactory, 从sessionFactory 获取 Session,实现数据库的

dubbo服务+spring动态数据源出错

1:问题描述,以及分析 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上. 这个问题在先调用读操作后再调用写操作会出现. 经日志分析原因: 第一:当程序运行一段时间后调用duboo服务时..([DubboServerHandler-192.168.1.106:20880-thread-199] [DubboServerHandler-192.168.1.106:20880-thread-200]) dubbo服务默认最大200线程(超过

spring动态数据源+事务

今天在尝试配置spring的动态数据源和事务管理的时候,遇到了几处配置上的问题,在此记录下: 1.使用了spring的aop思想,实现了动态数据源的切换. 2.spring的事务管理,是基于数据源的,也就是说Transaction是基于SessionFactory的, 所以如果要实现动态数据源切换,而且在同一个数据源中保证事务是起作用的话,就需要注意二者的顺序问题,即:在事务起作用之前就要把数据源切换回来. 3.application-mvc.xml文件扫描时不需要扫描services,避免事务

【sping揭秘】21、Spring动态数据源的切换

对于多个数据源的时候,我们如何切换不同的数据源进行数据库的操作呢? 当然我们可以直接定义2个DataSource,然后在每次获取connection的时候,从不同的DataSource中获取connection,类似如下 这种情况可以是2个数据库存放的数据性质是不同的,DataSource1存放1种数据,DataSource2存放另一种数据,每个数据库承担不同的数据访问请求,这2个是完全相互独立不相干的 这种就比较简单,那就是直接定义不同的jdbctemplate,设置不同的DataSource

spring动态切换数据源(一)

介绍下spring数据源连接的源码类:| 1 spring动态切换连接池需要类AbstractRoutingDataSource的源码 2 /* 3 * Copyright 2002-2017 the original author or authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in comp

Spring配置动态数据源-读写分离和多数据源

在现在互联网系统中,随着用户量的增长,单数据源通常无法满足系统的负载要求.因此为了解决用户量增长带来的压力,在数据库层面会采用读写分离技术和数据库拆分等技术.读写分离就是就是一个Master数据库,多个Slave数据库,Master数据库负责数据的写操作,slave库负责数据读操作,通过slave库来降低Master库的负载.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法

Spring 的动态数据源实现

1. 配置多个数据源 这里以两个c3p0数据库连接池的数据源作为实例.在Spring框架下使用c3p0的数据库需要加入c3p0-0.9.1.2.jar(现在最新的)这个支持包.这里以数据同步项目为例: 数据来源库的连接池数据源配置: Xml代码   <bean id="dataSourceFrom" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="dri

基于spring多数据源动态调用及其事务处理

需求: 有些时候,我们需要连接多个数据库,但是,在方法调用前并不知道到底是调用哪个.即同时保持多个数据库的连接,在方法中根据传入的参数来确定. 下图的单数据源的调用和多数据源动态调用的流程,可以看出在Dao层中需要有一个DataSource选择器,来确定到底是调用哪个数据源. 实现方式 对Dao层提供一个公共父类,保持有多个数据源的连接(本人是基于iBatis,即保持多个SQLSessionTemplate) /** * Created by hzlizhou on 2017/2/6. */ p