一、核心思想,spring提供了一個DataSource的子類,該類支持多個數據源java
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
該類的源碼以下:mysql
org.springframework.jdbc.datasource.lookup; java.sql.Connection; java.sql.SQLException; java.util.HashMap; java.util.Iterator; java.util.Map; java.util.Map.Entry; javax.sql.DataSource; org.springframework.beans.factory.InitializingBean; org.springframework.jdbc.datasource.AbstractDataSource; org.springframework.jdbc.datasource.lookup.DataSourceLookup; org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; org.springframework.util.Assert; AbstractRoutingDataSource AbstractDataSource InitializingBean { // 注意這裏,這裏使用Map存放多個數據源 Map<Object, Object> targetDataSources; ... // 篇幅有限,省略其餘代碼,有興趣的同窗能夠自行研究,仍是支持蠻多功能的,好比JNDI等數據源方式 // 注意這個暴露的方法,spring在從數據源中獲取數據庫鏈接時,經過這個方法肯定數據源 DataSource determineTargetDataSource() { Assert.notNull(.resolvedDataSources, ); Object lookupKey = .determineCurrentLookupKey(); DataSource dataSource = (DataSource).resolvedDataSources.get(lookupKey); (dataSource == && (.lenientFallback || lookupKey == )) { dataSource = .resolvedDefaultDataSource; } (dataSource == ) { IllegalStateException(+ lookupKey + ); } { dataSource; } } // 對外暴露的決定數據源的方法,經過外部設置數據源的key值, // spring自行從已存儲的數據源中查找指定數據源 Object determineCurrentLookupKey(); }
經過分析源碼,能夠知道咱們只須要實現如何動態設置切換數據源的方式便可,能夠考慮使用註解的方式在指定的位置添加數據源註解,利用AOP動態指定數據源。程序員
自定義的數據源以下:spring
com.yao.yz.yaowangdrug.dataSource; org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; DynamicDataSource AbstractRoutingDataSource{ Object determineCurrentLookupKey() { DataSourceHolder.(); } }
二、自定義註解,本章節不作過多解釋,對於註解有興趣的同窗請自行研究,廢話一句:程序員以實際應用爲主,研究行的內容請自行學習sql
com.yao.yz.yaowangdrug.dataSource; java.lang.annotation.*; (RetentionPolicy.) (ElementType.) @{ String value(); }
三、定義一個數據源切換功能組件,其中數據源存放的方式採用ThreadLocal的形式,我是單獨把存放作成一個額外的組件,這個是能夠自行決定的,代碼以下:數據庫
com.yao.yz.yaowangdrug.dataSource; DataSourceHolder { ThreadLocal<String> = InheritableThreadLocal<String>(); setDataSourceKey(String dataSource) { .set(dataSource); } Object getDataSourceKey() { .get(); } }
com.yao.yz.yaowangdrug.dataSource; org.apache.log4j.Logger; org.aspectj.lang.JoinPoint; org.aspectj.lang.reflect.MethodSignature; java.lang.reflect.Method; DynamicAspect { Logger = Logger.(DynamicAspect.); switchDataSource(JoinPoint point) NoSuchMethodException { Object target = point.getTarget(); String method = point.getSignature().getName(); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); Method m = classz[].getMethod(method, parameterTypes); (m != && m.isAnnotationPresent(.)) { data = m .getAnnotation(.); DataSourceHolder.(data.value()); } .info(+ DataSourceHolder.()); } }
四、在spring配置文件添加多個數據源,並將多個數據源統一歸入自定義數據源的管理,具體的配置信息以下:express
<!--裝載配置文件,將數據源的配置作成配置文件,方便管理,有興趣的同窗自行參考-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="Config.properties"/>
</bean>
<!--數據源1-->
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource1.jdbc.url}" />
<property name="user" value="${dataSource1.jdbc.username}" />
<property name="password" value="${dataSource1.jdbc.password}" />
<property name="minPoolSize" value="${dataSource1.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource1.jdbc.maxPoolSize}" />
</bean>
<!--數據源2-->
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource2.jdbc.url}" />
<property name="user" value="${dataSource2.jdbc.username}" />
<property name="password" value="${dataSource2.jdbc.password}" />
<property name="minPoolSize" value="${dataSource2.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource2.jdbc.maxPoolSize}" />
</bean>
<!--自定義數據源,將全部的數據源歸入自定義數據源管理-->
<bean id="dataSource" class="com.yao.yz.yaowangdrug.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map>
<!-- 對應spring提供的AbstractRoutingDataSource的Map -->
<entry key="dataSource1" value-ref="dataSource1"/>
<entry key="dataSource2" value-ref="dataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource1"/>
</bean>
五、配置數據源切換的AOP,代碼以下:apache
<!--動態決定數據源-->
<bean id="dataSourceSwitchAspect" class="com.yao.yz.yaowangdrug.dataSource.DynamicAspect"/>
<aop:config>
<aop:aspect id="dynamicAspect" ref="dataSourceSwitchAspect">
<!--數據源切換能夠控制爲Dao或者service層,請根據實際業務須要自行決定-->
<aop:pointcut id="dynamicPointCut" expression="execution(* com.xx.xx.xx.dao.*.*(..))"/>
<aop:before method="switchDataSource" pointcut-ref="dynamicPointCut"/>
</aop:aspect>
</aop:config>
六、使用spring整合mybatis,和通常的整合卻別在於使用的數據源爲自定義數據源,代碼以下:mybatis
classpath:ModelMapper.xml classpath:ModelMapper1.xml classpath:ModelMapper2.xml
注意:目前精力有限,此處爲何可使用自定義多個數據源的底層原理還沒來得及看,有興趣的同窗請自行研究,若是好心的話能夠簡單的告知我,謝謝你了~~~app
七、將自定義數據源歸入spring的事務管理器管理,配置代碼以下:
<!--事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--申明事務回滾的方法和異常信息-->
<tx:advice id="txAdvic" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*" read-only="false" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<!--申明事務範圍-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.xx.xx.xx.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvic" pointcut-ref="txPointCut"/>
</aop:config>
Model類:
com.yao.yz.yaowangdrug.model; DBModel { String ; String ; String getValue() { ; } setValue(String value) { .= value; } String getKey() { ; } setKey(String key) { .= key; } }
Dao接口:
com.yao.yz.yaowangdrug.dao; ; com.yao.yz.yaowangdrug.model.DBModel; DBDao { () insert(DBModel dbModel) Exception; }
Service接口:
com.yao.yz.yaowangdrug.service; com.yao.yz.yaowangdrug.model.DBModel; ; DBService { doInsert() Exception; doUpdate() Exception; testSeperateDB() Exception; }
關於事務的控制,有一下幾點說明:
一、採用申明或者註解實現事務控制時時,由於開啓了事務控制,因此若是是兩個不一樣的數據源Dao,根據spring的事務傳播特性,第二個事務開啓將使用已有的事務(即將採用第一個數據源的數據庫鏈接)進行事務操做,因此此時事務控制是失效的(即便切面執行了數據源切換)。結論就是跨數據庫的事務是沒法經過spring的數據庫控制實現的!!!請切記。
二、同一個數據源的事務控制和普通的數據源控制是一致的,沒有什麼區別。
以上代碼都是通過測試經過,能夠實現跨庫的數據方式,主要的應用場景是mysql的數據庫讀寫分離。若是不正確的地方請告知,謝謝!