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 compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  *      http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17
 18 package org.springframework.jdbc.datasource.lookup;
 19
 20 import java.sql.Connection;
 21 import java.sql.SQLException;
 22 import java.util.HashMap;
 23 import java.util.Map;
 24 import javax.sql.DataSource;
 25
 26 import org.springframework.beans.factory.InitializingBean;
 27 import org.springframework.jdbc.datasource.AbstractDataSource;
 28 import org.springframework.lang.Nullable;
 29 import org.springframework.util.Assert;
 30
 31 /**
 32  * Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
 33  * calls to one of various target DataSources based on a lookup key. The latter is usually
 34  * (but not necessarily) determined through some thread-bound transaction context.
 35  *
 36  * @author Juergen Hoeller
 37  * @since 2.0.1
 38  * @see #setTargetDataSources
 39  * @see #setDefaultTargetDataSource
 40  * @see #determineCurrentLookupKey()
 41  */
 42 public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
 43
 44     @Nullable
 45     private Map<Object, Object> targetDataSources;
 46
 47     @Nullable
 48     private Object defaultTargetDataSource;
 49
 50     private boolean lenientFallback = true;
 51
 52     private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
 53
 54     @Nullable
 55     private Map<Object, DataSource> resolvedDataSources;
 56
 57     @Nullable
 58     private DataSource resolvedDefaultDataSource;
 59
 60
 61     / * *
 62         *指定目标数据源的映射,查找键为键。
 63         *映射的值可以是对应的{@link javax.sql.DataSource}
 64          实例或数据源名称字符串(通过{@link setDataSourceLookup DataSourceLookup}解析)。
 65         * <p>密钥可以是任意类型;该类只实现泛型查找过程。
 66         具体的键表示将由{
 67         @link # resolvespeciedlookupkey (Object)}和{@link #行列式urrentlookupkey()}处理。
 68         * /
 69     public void setTargetDataSources(Map<Object, Object> targetDataSources) {
 70         this.targetDataSources = targetDataSources;
 71     }
 72
 73
 74
 75
 76      / * *
 77      *指定默认的目标数据源(如果有的话)。
 78      * <p>映射值可以是对应的
 79      {@link javax.sql.DataSource}
 80      实例或数据源名称字符串
 81      (通过{@link #setDataSourceLookup DataSourceLookup}解析)。
 82      * <p>如果key {@link #setTargetDataSources targetDataSources}
 83      没有匹配{@link #决定ecurrentlookupkey()}当前查找键,
 84      则此数据源将被用作目标。
 85      * /
 86
 87     public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
 88         this.defaultTargetDataSource = defaultTargetDataSource;
 89     }
 90
 91     / * *
 92       *指定是否对默认数据源应用宽松的回退
 93       *如果无法为当前查找键找到特定的数据源。
 94       * <p>默认为“true”,接受在目标数据源映射中没有对应条目的查找键——在这种情况下,简单地返回到默认数据源。
 95       * <p>将此标志切换为“false”,如果您希望回退仅在查找键为{@code null}时应用。
 96       没有数据源项的查找键将导致IllegalStateException。
 97       * @see # setTargetDataSources
 98       * @see # setDefaultTargetDataSource
 99       * @see # determineCurrentLookupKey ()
100        * /
101     public void setLenientFallback(boolean lenientFallback) {
102         this.lenientFallback = lenientFallback;
103     }
104
105     / * *
106         *设置DataSourceLookup实现来解析数据源
107         {@link #setTargetDataSources targetDataSources}映射中的名称字符串。
108         * <p>默认是{@link JndiDataSourceLookup},允许JNDI名称
109          *直接指定的应用服务器数据源。
110         * /
111     public void setDataSourceLookup(@Nullable DataSourceLookup dataSourceLookup) {
112         this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
113     }
114
115
116     @Override
117     public void afterPropertiesSet() {
118         if (this.targetDataSources == null) {
119             throw new IllegalArgumentException("Property ‘targetDataSources‘ is required");
120         }
121         this.resolvedDataSources = new HashMap<>(this.targetDataSources.size());
122         this.targetDataSources.forEach((key, value) -> {
123             Object lookupKey = resolveSpecifiedLookupKey(key);
124             DataSource dataSource = resolveSpecifiedDataSource(value);
125             this.resolvedDataSources.put(lookupKey, dataSource);
126         });
127         if (this.defaultTargetDataSource != null) {
128             this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
129         }
130     }
131
132     / * *
133         *将给定的查找键对象
134         *(如{@link #setTargetDataSources targetDataSources}映射中指定的)解析为实际的查找键,
135         *用于与{@link #决定ecurrentlookupkey()当前查找键}匹配。
136         * <p>默认实现只是简单地返回给定的键值。
137         * @param lookupKey用户指定的查找键对象
138         * @根据需要返回查找键以进行匹配
139         * /
140     protected Object resolveSpecifiedLookupKey(Object lookupKey) {
141         return lookupKey;
142     }
143
144
145 / * *
146 *将指定的数据源对象解析为数据源实例。
147 * <p>默认实现处理数据源实例和数据源名称(通过{@link #setDataSourceLookup DataSourceLookup}解析)。
148 {@link #setTargetDataSources targetDataSources}映射中指定的数据源值对象
149 * @返回已解析的数据源(绝不是{@code null})
150 * @抛出不支持的值类型的IllegalArgumentException
151 * /
152
153     protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
154         if (dataSource instanceof DataSource) {
155             return (DataSource) dataSource;
156         }
157         else if (dataSource instanceof String) {
158             return this.dataSourceLookup.getDataSource((String) dataSource);
159         }
160         else {
161             throw new IllegalArgumentException(
162                     "Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
163         }
164     }
165
166
167     @Override
168     public Connection getConnection() throws SQLException {
169         return determineTargetDataSource().getConnection();
170     }
171
172     @Override
173     public Connection getConnection(String username, String password) throws SQLException {
174         return determineTargetDataSource().getConnection(username, password);
175     }
176
177     @Override
178     @SuppressWarnings("unchecked")
179     public <T> T unwrap(Class<T> iface) throws SQLException {
180         if (iface.isInstance(this)) {
181             return (T) this;
182         }
183         return determineTargetDataSource().unwrap(iface);
184     }
185
186     @Override
187     public boolean isWrapperFor(Class<?> iface) throws SQLException {
188         return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
189     }
190
191
192       /**
193        *检索当前目标数据源。决定了
194        * {@link # definecurrentlookupkey()当前查找键},在{@link #setTargetDataSources targetDataSources}映射中执行查找,返回指定的
195        * {@link #setDefaultTargetDataSource默认目标数据源}如果需要。
196        * @see # determineCurrentLookupKey ()
197
198 通多debug会发现DataSource dataSource = this.resolvedDataSources.get(lookupKey);非常关键。获取数据源类似key的标识
199
200        */
201
202     protected DataSource determineTargetDataSource() {
203         Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
204         Object lookupKey = determineCurrentLookupKey();
205         DataSource dataSource = this.resolvedDataSources.get(lookupKey);
206         if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
207             dataSource = this.resolvedDefaultDataSource;//没有数据源设置为默认的数据源
208         }
209         if (dataSource == null) {//没切换数据源并且没有设置默认数据源,此处抛出异常
210             throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
211         }
212         return dataSource;//返回数据源对象
213     }
214
215     /*
216          * 确定当前查找键。这通常是
217          *用于检查线程绑定的事务上下文。
218          * <p>允许任意键。返回的密钥需要方法解析后匹配存储的查找键类型
219          * {@link # resolvespeciedlookupkey}方法。
220          */
221     @Nullable
222     protected abstract Object determineCurrentLookupKey();//抽像方法,需要重写然后在protected DataSource determineTargetDataSource() 中调用
223
224 }

源码中关键类的介绍:

上面的方法走完后下辖一步debug就是获取驱动连接:

数据源切换代码标记图:

下面是配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
    ">

    <!--原始默认数据源配置C3P0-->

    <!--<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  destroy-method="close">-->
    <!--<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>-->
    <!--<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/quanxian?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true"/>-->
    <!--<property name="user" value="wangbiao"/>-->
    <!--<property name="password" value="[email protected]"/>-->
    <!--&lt;!&ndash;默认为0,单位为秒,表示在连接池中未被使用的连接最长存活多久不被移除&ndash;&gt;-->
    <!--<property name="maxIdleTime" value="3600"/>-->
    <!--&lt;!&ndash;默认为3表示连接池中任何时候可以存放的连接最小数量。&ndash;&gt;-->
    <!--<property name="minPoolSize" value="1"/>-->
    <!--&lt;!&ndash; 默认为15,表示连接池中任何时候可以存放的连接最大数量。&ndash;&gt;-->
    <!--<property name="maxPoolSize" value="5"/>-->
    <!--&lt;!&ndash;默认为3,表示初始化连接池时获取的连接个数。该数值在miniPoolSize和maxPoolSize之间。&ndash;&gt;-->
    <!--<property name="initialPoolSize" value="2"/>-->
    <!--&lt;!&ndash;表示当连接池中连接用完时,客户端调用getConnection获取连接等待的时间 如果超时,则抛出SQLException异常。特殊值0表示无限期等待&ndash;&gt;-->
    <!--<property name="checkoutTimeout" value="4800000"/>-->
    <!--</bean>-->

    <!--数据源0-->
    <bean id="dataSource0" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/quanxian?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="wangbiao"/>
        <property name="password" value="[email protected]"/>
    </bean>

<!--数据源1-->
    <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/qrtz_timer?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="[email protected]"/>
    </bean>

    <!--多数据源配置-->
    <bean id="multiDataSource" class="com.ry.project.dataSouces.DynamicDataSource">
        <property name="targetDataSources">
            <map>
                <entry key="dataSource0" value-ref="dataSource0"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSource1"></property>
    </bean>

    <!--<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
        <!--<property name="dataSource" ref="multiDataSource"/>-->
        <!--&lt;!&ndash;<property name="configLocation" value="classpath:mybatis-config.xml"/>&ndash;&gt;-->
        <!--<property name="mapperLocations" value="classpath*:/mapper/User.xml"/>-->
    <!--</bean>-->

     <!--会话工厂-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="multiDataSource"/>
        <!--<property name="configLocation" value="classpath:mybatis-config.xml"/>-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <!--使用下面的方式配置参数,一行配置一个 -->
                        <value>
                            helperDialect=mysql
                            reasonable=true
                            supportMethodsArguments=true
                            params=count=countSql
                            autoRuntimeDialect=true
                        </value>
                    </property>
                </bean>
            </array>
        </property>
        <property name="mapperLocations" value="classpath:mapper/*.xml" />
    </bean>

    <!--mybatis扫描 映射-->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ry.project.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
    </bean>

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

</beans>

在spring配置文件中加上这个Order管控事务与AOP顺序问题,防止实物卡住数据源无法切换:

    <tx:annotation-driven transaction-manager="transactionManager" order="2"/>

下面是我的java代码:相关类引用网友:

https://blog.csdn.net/u013034378/article/details/82469368

 1 package com.ry.project.dataSouces;
 2
 3 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 4
 5 public class DynamicDataSource extends AbstractRoutingDataSource {
 6
 7     /* ThreadLocal,叫线程本地变量或线程本地存储。
 8      * ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
 9      * 这里使用它的子类InheritableThreadLocal用来保证父子线程都能拿到值。
10      */
11     private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
12
13     /**
14      * 设置dataSourceKey的值
15      * @param dataSource
16      */
17     public static void setDataSourceKey(String dataSource) {
18         dataSourceKey.set(dataSource);
19     }
20     /**
21      * 清除dataSourceKey的值
22      */
23     public static void toDefault() {
24         dataSourceKey.remove();
25     }
26
27     @Override
28     protected Object determineCurrentLookupKey() {
29         return dataSourceKey.get();
30     }
31     /**
32      * 返回当前dataSourceKey的值
33      */
34
35
36 }

 1 package com.ry.project.dataSouces;
 2
 3 import java.lang.annotation.*;
 4
 5 @Target({ElementType.METHOD,ElementType.TYPE})
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface DynamicRoutingDataSource {
 9     String value() default "dataSource1";//本文默认dataSource
10 }

 1 package com.ry.project.dataSouces;
 2
 3 import org.aspectj.lang.JoinPoint;
 4 import org.aspectj.lang.annotation.After;
 5 import org.aspectj.lang.annotation.Aspect;
 6 import org.aspectj.lang.annotation.Before;
 7 import org.aspectj.lang.annotation.Pointcut;
 8 import org.springframework.core.Ordered;
 9 import org.springframework.stereotype.Component;
10
11 import java.lang.reflect.Method;
12
13 @Aspect
14 @Component
15 public class HandlerDataSourceAop implements Ordered {
16
17     /**
18      * @within匹配类上的注解
19      * @annotation匹配方法上的注解
20      */
21     @Pointcut("@within(com.ry.project.dataSouces.DynamicRoutingDataSource)||@annotation(com.ry.project.dataSouces.DynamicRoutingDataSource)")
22     public void pointcut(){}
23
24     @Before(value = "pointcut()")
25     public void beforeOpt(JoinPoint joinPoint) throws NoSuchMethodException {
26         /** 先查找方法上的注解,没有的话再去查找类上的注解
27          *-----------------------------------------------------------------------
28          * 这里使用的是接口的模式,注解在实现类上,所以不能使用如下方式获取目标方法的对象,
29          * 因为该方式获取的是该类的接口或者顶级父类的方法的对象.
30          * MethodSignature methodSignature = (MethodSignature)point.getSignature();
31          * Method method = methodSignature.getMethod();
32          * DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
33          * 通过上面代码是获取不到方法上的注解的,如果真要用上面代码来获取,可以修改aop代理模式,修改为cglib代理
34          * 在xml配置文件修改为<aop:aspectj-autoproxy proxy-target-class="true" /> ,
35          * proxy-target-class属性true为cglib代理,默认false为jdk动态代理 。
36          * ---------------------------------------------------------
37          * 本文使用是jdk动态代理, 这里使用反射的方式获取方法
38          */
39         //反射获取Method 方法一
40         Object target = joinPoint.getTarget();
41         Class<?> clazz = target.getClass();
42         Method[] methods = clazz.getMethods();
43         DynamicRoutingDataSource annotation = null;
44         for (Method method : methods) {
45             if (joinPoint.getSignature().getName().equals(method.getName())) {
46                 annotation = method.getAnnotation(DynamicRoutingDataSource.class);
47                 if (annotation == null) {
48                     annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
49                     if (annotation == null) {
50                         return;
51                     }
52                 }
53             }
54         }
55
56
57 //             反射获取Method 方法二
58 //                Object[] args = joinPoint.getArgs();
59 //                Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
60 //                for (int i = 0; i < args.length; i++) {
61 //                    argTypes[i] = args[i].getClass();
62 //                }
63 //                Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
64 //                DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
65 //                if (annotation == null) {
66 //                    annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
67 //                    if (annotation == null) {
68 //                        return;
69 //                    }
70 //                }
71
72         String dataSourceName = annotation.value();
73         DynamicDataSource.setDataSourceKey(dataSourceName);
74         System.out.println("切到" + dataSourceName + "数据库");
75     }
76     @After(value="pointcut()")
77     public void afterOpt(){
78         DynamicDataSource.toDefault();
79         System.out.println("切回默认数据库");
80     }
81
82     @Override
83     public int getOrder() {
84         return 1;
85     }
86 }

原文地址:https://www.cnblogs.com/wangbiaohistory/p/12545415.html

时间: 2024-10-08 09:42:17

spring动态切换数据源(一)的相关文章

Spring + Mybatis 项目实现动态切换数据源

项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. 参考了两篇文章如下: http://blog.csdn.net/zl3450341/article/details/20150687 http://www.blogjava.net/hoojo/archive/2013/10/22/405488.html 这两篇文章都对原理进行了分析,下面只写自己

基于spring+mybatis+atomikos+jta实现分布式事务(2)-动态切换数据源

本文介绍基于spring+mybatis+atomikos+jta实现分布式事务,由程序动态切换数据源,通过atomikos可实现分布式事务一致性. 版本:spring-3.2.9.RELEASE.mybatis-3.4.4.atomikos-4.0.5.jdk1.8 1,maven配置文件pom.xml如下: <!-- test --> <dependency> <groupId>junit</groupId> <artifactId>juni

Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法(转)

一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基本上没有什么问题,但唯一可能出现问题的就是在hibernate做添加操作生成主键策略的时候.因为我们都知道hibernate的数据库本地方言会针对不同的数据库采用不同的主键生成策略. 所以针对这一问题不得不采用自定义的主键生成策略,自己写一个主键生成器的表来维护主键生成方式或以及使用其他的方式来生成

hibernate动态切换数据源

起因: 公司的当前产品,主要是两个项目集成的,一个是java项目,还有一个是php项目,两个项目用的是不同的数据源,但都是mysql数据库,因为java这边的开发工作已经基本完成了,而php那边任务还很多,人手也比较紧,产品上线日期紧促,所以领导希望java这边能够帮助php那边写接口,所以需要切换数据源 思路: 动态切换数据源确切的来说是在同一类型数据库的情况下的.意思就是说 , 在系统中的使用的数据库分布在多台数据库服务器或者在同台服务器上的多个数据库. 在运行时期间根据某种标识符来动态的选

Spring动态多数据源实例Demo

最近由于咨询Spring如何配置多数据源的人很多,一一回答又比较麻烦,而且以前的博文中的配置也是有问题,因此特此重新发布一个Demo给大家. Demo中共有两个数据源,即Mysql和Oracle,并已经进行简单测试,动态切换数据源是没有问题的,希望借此Demo能帮助到大家. Demo下载地址: Spring动态切换多数据源Demo:http://download.csdn.net/download/wangpeng047/8419953 另外我给些说明,阐述下多数据源配置时的重点: 1. 注意事

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

(其中@Order(1)作用: Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟spring的事务aop执行的先后顺序问题,比如说动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了Order(排序)这个关键字.) @Order(1) @Aspect @Repository public class DataSourceAspect { @Pointcut("execution(* com.xxx.service.impl.*.*

Spring MVC动态切换数据源(多数据库类型)

最近由于项目需求,需要将Sql Server 和 Mysql 两种数据库整合到一个项目,项目的用到的框架是SSM. 因此尝试了利用AOP切面来切每次执行的Servcie方法,根据Service所在的包名来实现数据源自动切换. 1.项目架构如下: 2.在com.jiefupay.database包中建立四个类: 其中 DataSourceContextHolder.java类源码如下: package com.jiefupay.datebase; public class DataSourceCo

Spring代码中动态切换数据源

在Spring-Mybatis中,有这样一个类AbstractRoutingDataSource,根据名字可以猜到,这是一个框架提供的用于动态选择数据源的类.这个类有两个重要的参数,分别叫 defaultTargetDataSource和targetDataSources.一般的工程都是一个数据源,所以不太接触到这个类. [html] <bean id="myoneDataSource" class="org.apache.commons.dbcp2.BasicData

Spring动态切换多数据源解决方案

spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据. Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来.Client提供选择所需的上下文(因为