1.場景,實現數據庫的讀寫分離。java
2.思路,既然是讀寫分離,那就是須要切換不一樣的數據源,一種是靜態切換,就是提早配置好兩個靜態數據庫資源,還有一種就是動態的切換資源,這裏用到spring,那就要知道spring如何動態的切換數據源。mysql
3.spring提供了動態切換數據源接口AbstractRoutingDataSource,關於AbstractRoutingDataSource這個類咱們能夠看下它的源碼web
protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; }
能夠看到會調用determineCurrentLookupKey()這個用方法去獲取key,而後根據key去獲取對應的數據源,這個時候咱們就能夠集成這個重寫這個方法代碼以下:spring
public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { // TODO Auto-generated method stub return DynamicDataSourceHolder.getDataSource(); } }
DynamicDataSourceHolder類:sql
public class DynamicDataSourceHolder { public final static ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String name){ holder.set(name); } public static String getDataSource(){ return holder.get(); } }
使用ThreadLocal是爲了防止併發帶來的問題,保證每一個線程用到的是本身的數據源。數據庫
上面獲取key的方法會在下面會用。編程
3.解決了動態獲取數據源下面就是,如何實現當我調用讀方法時切換到讀數據源,實現寫操做時切換到寫數據源。併發
這裏就能夠用到spring aop 面向切面編程,爲了方便操做咱們能夠寫一個自定註解使用自定義的註解去註解那個方法是讀操做那個方法是寫操做app
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { String value(); }
aop類:maven
@Component @Aspect public class DaoAspect { @Pointcut("execution(* com.kedacom.aop.service.*.*(..))") public void read(){} @Before("read()") public void beforeRead(JoinPoint point){ Object object = point.getTarget(); //獲取方法名稱 String methodName = point.getSignature().getName(); Class<?>[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes(); try { //獲取到方法 Method method = object.getClass().getMethod(methodName, parameterTypes); //獲取註解中的值 DataSource dataSource = method.getAnnotation(DataSource.class); //獲取主從數據庫的key以便切換數據庫 String dbKey = dataSource.value(); DynamicDataSourceHolder.putDataSource(dbKey); System.out.println(dbKey); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }
這裏使用 到aop的註解功能。
兩個服務了類:
@Service("readService") public class ReadService { @Resource(name="userDao") UserDao userDao; @DataSource(value="slave") public User readUser(String id){ return userDao.getUser(id); } }
@Service("writeService") public class WriteService { @Resource(name="userDao") UserDao userDao; @DataSource(value="master") public void writeDb(User user){ userDao.save(user); } }
測試代碼:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); // PersonServer bean = (PersonServer)ctx.getBean("personServiceBean"); // bean.save("fasdfa"); WriteService ws = (WriteService)ctx.getBean("writeService"); User user = new User(); user.setId("123"); user.setName("xxx"); user.setPasswd("ok"); ws.writeDb(user); ReadService rs = (ReadService)ctx.getBean("readService"); User u = rs.readUser("1"); System.out.println(u.getId()+"--"+u.getName()+"--"+u.getPasswd()); }
spring的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" 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:dwr="http://www.directwebremoting.org/schema/spring-dwr" xmlns:dwra="http://www.directwebremoting.org/schema/spring-dwr-annotations" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd http://www.directwebremoting.org/schema/spring-dwr-annotations http://www.directwebremoting.org/schema/spring-dwr-annotations.xsd "> <context:annotation-config /> <context:component-scan base-package="com.guo.*,com.kedacom.*" /> <aop:aspectj-autoproxy/> <bean id="personServiceBean" class="com.guo.test.PersonServerBean"/> <!-- <bean id="myInterceptor" class="com.guo.test.MyInterceptor"/> --> <!-- 主數據庫源 --> <bean id="masterDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="jdbc:mysql://localhost:3306/maven?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> </bean> <!-- 從數據庫源 --> <bean id="slaveDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="jdbc:mysql://localhost:3306/test_user?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> </bean> <!-- 配置動態數據源 --> <bean id="dataSource" class="com.kedacom.aop.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- write --> <entry key="master" value-ref="masterDataSource"/> <!-- read --> <entry key="slave" value-ref="slaveDataSource"/> </map> </property> <!-- 默認數據源 --> <property name="defaultTargetDataSource" ref="masterDataSource"/> </bean> <bean id="daoJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> </beans>