使用Spring AOP实现MySQL读写分离

spring aop , mysql 主从配置 实现读写分离,下来把自己的配置过程,以及遇到的问题记录下来,方便下次操作,也希望给一些朋友带来帮助。
mysql主从配置参看:http://blog.csdn.net/huoyunshen88/article/details/26597483
1.使用spring aop 拦截机制现数据源的动态选取。

 
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface DataSource {
  4. String value();
  5. }

3.利用Spring的AbstractRoutingDataSource解决多数据源的问题 参考本站《使用Spring的AbstractRoutingDataSource解决多数据源的问题》

 
  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  2.  
  3. public class ChooseDataSource extends AbstractRoutingDataSource {
  4.  
  5. @Override
  6. protected Object determineCurrentLookupKey() {
  7. return HandleDataSource.getDataSource();
  8. }
  9. }

4.利用ThreadLocal解决线程安全问题

 
  1. public class HandleDataSource {
  2. public static final ThreadLocal<String> holder = new ThreadLocal<String>();
  3. public static void putDataSource(String datasource) {
  4. holder.set(datasource);
  5. }
  6.  
  7. public static String getDataSource() {
  8. return holder.get();
  9. }
  10. }

5.定义一个数据源切面类,通过aop访问,在spring配置文件中配置了,所以没有使用aop注解

 
  1. import java.lang.reflect.Method;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.springframework.stereotype.Component;
  8. //@Aspect
  9. //@Component
  10. public class DataSourceAspect {
  11. //@Pointcut("execution(* com.apc.cms.service.*.*(..))")
  12. public void pointCut(){};
  13.  
  14. // @Before(value = "pointCut()")
  15. public void before(JoinPoint point)
  16. {
  17. Object target = point.getTarget();
  18. System.out.println(target.toString());
  19. String method = point.getSignature().getName();
  20. System.out.println(method);
  21. Class<?>[] classz = target.getClass().getInterfaces();
  22. Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
  23. .getMethod().getParameterTypes();
  24. try {
  25. Method m = classz[0].getMethod(method, parameterTypes);
  26. System.out.println(m.getName());
  27. if (m != null && m.isAnnotationPresent(DataSource.class)) {
  28. DataSource data = m.getAnnotation(DataSource.class);
  29. HandleDataSource.putDataSource(data.value());
  30. }
  31.  
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }

6.配置applicationContext.xml

 
  1. <!-- 主库数据源 -->
  2. <bean id="writeDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
  3. <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  4. <property name="jdbcUrl" value="jdbc:mysql://172.22.14.6:3306/cpp?autoReconnect=true"/>
  5. <property name="username" value="root"/>
  6. <property name="password" value="root"/>
  7. <property name="partitionCount" value="4"/>
  8. <property name="releaseHelperThreads" value="3"/>
  9. <property name="acquireIncrement" value="2"/>
  10. <property name="maxConnectionsPerPartition" value="40"/>
  11. <property name="minConnectionsPerPartition" value="20"/>
  12. <property name="idleMaxAgeInSeconds" value="60"/>
  13. <property name="idleConnectionTestPeriodInSeconds" value="60"/>
  14. <property name="poolAvailabilityThreshold" value="5"/>
  15. </bean>
  16.  
  17. <!-- 从库数据源 -->
  18. <bean id="readDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
  19. <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  20. <property name="jdbcUrl" value="jdbc:mysql://172.22.14.7:3306/cpp?autoReconnect=true"/>
  21. <property name="username" value="root"/>
  22. <property name="password" value="root"/>
  23. <property name="partitionCount" value="4"/>
  24. <property name="releaseHelperThreads" value="3"/>
  25. <property name="acquireIncrement" value="2"/>
  26. <property name="maxConnectionsPerPartition" value="40"/>
  27. <property name="minConnectionsPerPartition" value="20"/>
  28. <property name="idleMaxAgeInSeconds" value="60"/>
  29. <property name="idleConnectionTestPeriodInSeconds" value="60"/>
  30. <property name="poolAvailabilityThreshold" value="5"/>
  31. </bean>
  32.  
  33. <!-- transaction manager, 事务管理 -->
  34. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  35. <property name="dataSource" ref="dataSource" />
  36. </bean>
  37.  
  38.  
  39. <!-- 注解自动载入 -->
  40. <context:annotation-config />
  41.  
  42. <!--enale component scanning (beware that this does not enable mapper scanning!)-->
  43. <context:component-scan base-package="com.apc.cms.persistence.rdbms" />
  44. <context:component-scan base-package="com.apc.cms.service">
  45. <context:include-filter type="annotation"
  46. expression="org.springframework.stereotype.Component" />
  47. </context:component-scan>
  48.  
  49. <context:component-scan base-package="com.apc.cms.auth" />
  50.  
  51. <!-- enable transaction demarcation with annotations -->
  52. <tx:annotation-driven />
  53.  
  54.  
  55. <!-- define the SqlSessionFactory -->
  56. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  57. <property name="dataSource" ref="dataSource" />
  58. <property name="typeAliasesPackage" value="com.apc.cms.model.domain" />
  59. </bean>
  60.  
  61. <!-- scan for mappers and let them be autowired -->
  62. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  63. <property name="basePackage" value="com.apc.cms.persistence" />
  64. <property name="sqlSessionFactory" ref="sqlSessionFactory" />
  65. </bean>
  66.  
  67. <bean id="dataSource" class="com.apc.cms.utils.ChooseDataSource">
  68. <property name="targetDataSources">
  69. <map key-type="java.lang.String">
  70. <!-- write -->
  71. <entry key="write" value-ref="writeDataSource"/>
  72. <!-- read -->
  73. <entry key="read" value-ref="readDataSource"/>
  74. </map>
  75.  
  76. </property>
  77. <property name="defaultTargetDataSource" ref="writeDataSource"/>
  78. </bean>
  79.  
  80. <!-- 激活自动代理功能 -->
  81. <aop:aspectj-autoproxy proxy-target-class="true"/>
  82.  
  83. <!-- 配置数据库注解aop -->
  84. <bean id="dataSourceAspect" class="com.apc.cms.utils.DataSourceAspect" />
  85. <aop:config>
  86. <aop:aspect id="c" ref="dataSourceAspect">
  87. <aop:pointcut id="tx" expression="execution(* com.apc.cms.service..*.*(..))"/>
  88. <aop:before pointcut-ref="tx" method="before"/>
  89. </aop:aspect>
  90. </aop:config>
  91. <!-- 配置数据库注解aop -->

7.使用注解,动态选择数据源,分别走读库和写库

 
  1. @DataSource("write")
  2. public void update(User user) {
  3. userMapper.update(user);
  4. }
  5.  
  6. @DataSource("read")
  7. public Document getDocById(long id) {
  8. return documentMapper.getById(id);
  9. }
测试写操作:可以通过应用修改数据,修改主库数据,发现从库的数据被同步更新了,所以定义的write操作都是走的写库
测试读操作: 后台修改从库数据,查看主库的数据没有被修改,在应用页面中刷新,发现读的是从库的数据,说明读写分离ok。


http://www.sunyaozong.com/mysql-read-write-sepreate-solution-by-spring-aop/

posted @ 2017-10-06 17:08  jack_ou  阅读(757)  评论(0编辑  收藏  举报