實現流程java
1.ssm基本流程 1.1 一個http請求,被springMVC的DispatcherServlet攔截,而後交給HandleRequest處理。 1.2 HandleRequest調用HandlerMapping與HandlerAdapter兩個類,去匹配controller上的路徑。 1.3 一旦匹配上,就執行該路徑對應的方法。 1.4 controller層只是起一個控制做用,實際的業務處理交給service實現層。 1.5 實現層處理完數據,將結果返回給controller層。 這是一個基本的ssm流程 2.實現讀寫分離 2.1 配置一個動態數據源切面,將該切面添加到service上。 2.2 一旦有請求進來,執行到service層,該切面進行攔截,可以獲取到請求方法的名稱和參數。 2.3 這裏咱們只須要方法名稱就夠了,實際上這裏拿到的方法名稱與請求參數能夠作緩存(便是spring緩存原理)。該切面完成的功能是:根據執行方法名稱判斷應該走讀庫仍是走寫庫,這裏返回的是一個標記(讀庫仍是寫庫的標記)。 2.4 接着有一個動態數據源的bean,該bean繼承spring的AbstractRoutingDataSource類,而且覆寫該類的determineCurrentLookupKey方法,返回讀寫庫標記。同時該bean配置在spring-mybatis.xml文件中,因爲該bean繼承自AbstractRoutingDataSource類,該類有個屬性targetDataSources,這個屬性的類型是map,在這個map中設置讀寫標記對應讀寫數據源,根據剛剛傳進來的標記來匹配該map中的key值,根據這個key值走不一樣的數據源。 2.5 spring-mybatis.xml中配置兩套數據源,上述的key值就是走的這兩套數據源中的一個。
本文基於另外一篇文章
http://www.javashuo.com/article/p-sxtutbnw-dq.html
spring讀寫分離其實就是mysql主從複製,只不過添加了動態切換數據源的功能。
參考文章
http://blog.csdn.net/jack85986370/article/details/51559232
這一篇好像更加不錯
http://blog.csdn.net/u013632755/article/details/51557956#comments
###1.完成搭建ssm最小系統
參見我這篇文章 http://www.javashuo.com/article/p-avkcqvwb-em.html
###2.添加master包
###3.DataSourceAspectmysql
package com.test.spring.master; import org.aspectj.lang.JoinPoint; public class DataSourceAspect { /** * 進入service方法以前 */ public void before2(JoinPoint point) { String methodName = point.getSignature().getName(); if (isSlave(methodName)) { //走寫庫 DynamicDataSourceHolder.markMaster(); }else { //走讀庫 DynamicDataSourceHolder.markSlave(); } } /** * 判斷是否爲讀庫 */ private Boolean isSlave(String methodName) { // 方法名以query、find、get開頭的方法名走從庫 boolean flag = true; if (methodName.contains("query")|| methodName.contains("find") || methodName.contains("get")) { flag = false; } return flag; } }
###4.DynamicDataSourcespring
package com.test.spring.master; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { System.out.println("----------DynamicDataSource---------"); return DynamicDataSourceHolder.getDataSourceKey(); } }
###5.DynamicDataSourceHoldersql
package com.test.spring.master; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DynamicDataSourceHolder { private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceHolder.class); //寫庫對應的數據源key private static final String MASTER = "master"; //讀庫對應的數據源key private static final String SLAVE = "slave"; //使用ThreadLocal記錄當前線程的數據源key private static final ThreadLocal<String> holder = new ThreadLocal<String>(); /** * 設置數據源key * @param key */ public static void putDataSourceKey(String key) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("------切換數據源,key:"+key+"-------"); } holder.set(key); } /** * 獲取數據源key * @return */ public static String getDataSourceKey() { return holder.get(); } /** * 標記寫庫 */ public static void markMaster(){ putDataSourceKey(MASTER); } /** * 標記讀庫 */ public static void markSlave(){ putDataSourceKey(SLAVE); } }
###6.db.properties數據庫
driverClassName=com.mysql.jdbc.Driver #主數據庫 master_jdbc_url=jdbc:mysql://192.168.2.11:3306/test?useUnicode=true&characterEncoding=UTF-8 master_jdbc_username=root master_jdbc_password=123456 #從數據庫 slave_jdbc_url=jdbc:mysql://192.168.2.11:3307/test?useUnicode=true&characterEncoding=UTF-8 slave_jdbc_username=root slace_jdbc_password=654321
注意:mysql搭建在同一臺主機上,僅僅端口不一樣
###7.spring-mybatis.xmlexpress
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd"> <!-- 讀取配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置master數據源 --> <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${driverClassName}"></property> <property name="url" value="${master_jdbc_url}"></property> <property name="username" value="${master_jdbc_username}"></property> <property name="password" value="${master_jdbc_password}"></property> </bean> <!-- 配置slave數據源 --> <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${driverClassName}"></property> <property name="url" value="${slave_jdbc_url}"></property> <property name="username" value="${slave_jdbc_username}"></property> <property name="password" value="${slace_jdbc_password}"></property> </bean> <!-- 動態數據源 --> <bean id="dataSource" class="com.test.spring.master.DynamicDataSource"> <!-- 設置多個數據源 --> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 這裏的key要和程序中保持一致 --> <entry key="master" value-ref="masterDataSource"></entry> <entry key="slave" value-ref="slaveDataSource"></entry> </map> </property> <!-- 默認數據源 --> <property name="defaultTargetDataSource" ref="masterDataSource"></property> </bean> <!-- 配置sqlSessionFactory --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 數據源 --> <property name="dataSource" ref="dataSource" /> <!-- 自動掃描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/test/spring/mapper/*.xml" /> </bean> <!-- 配置mapper掃描器 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 掃描包路徑,若是須要掃描多個包,用英文 逗號隔開 --> <property name="basePackage" value="com.test.spring.mapper" /> <!-- 關聯mapper掃描器 與 sqlsession管理器 --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /> </bean> <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事務屬性 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 定義查詢方法只讀 --> <tx:method name="query*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="get*" read-only="true"/> <!-- 主數據庫操做 --> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <!-- 其它方法使用默認事務策略 --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <aop:pointcut expression="execution(* com.test.spring.service.impl.*Impl.*(..))" id="txPointcut"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config> <!-- 切面bean配置 --> <bean id="dataSourceAspect" class="com.test.spring.master.DataSourceAspect"></bean> <aop:config> <aop:pointcut expression="execution(* com.test.spring.service.*.*(..))" id="aspectPointcut"/> <aop:aspect ref="dataSourceAspect" order="-9999"> <aop:before method="before2" pointcut-ref="aspectPointcut"/> </aop:aspect> </aop:config> </beans>
###8.實現層緩存
package com.test.spring.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.test.spring.bean.User; import com.test.spring.mapper.UserMapper; import com.test.spring.service.UserService; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; /** * 獲取用戶列表 */ @Override public void getUserList() { System.out.println("走 從數據庫"); List<User> listUser = userMapper.getUserList(); System.out.println(listUser); System.out.println("================"); } /** * 更新用戶信息 */ @Override public void updateUser(String userId,String userName) throws Exception{ System.out.println("走 主數據庫"); int sucess = userMapper.updateUser(userId, userName); System.out.println(sucess); throw new RuntimeException(); } }
###9.測試
親測,
1.能夠實現動態切換數據庫
2.事務也能夠起效
3.主從同步在mysql那一篇已經實現session