天天看點

使用spring aop實作業務層mysql 讀寫分離

spring aop , mysql 主從配置 實作讀寫分離,下來把自己的配置過程,以及遇到的問題記錄下來,友善下次操作,也希望給一些朋友帶來幫助。

mysql主從配置參看:

​​javascript:void(0)​​

1.使用spring aop 攔截機制現資料源的動态選取。

[html] 

​​view plain​​

​​copy​​

​​

​​

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Target;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. /**
  6. * RUNTIME
  7. * 編譯器将把注釋記錄在類檔案中,在運作時 VM 将保留注釋,是以可以反射性地讀取。
  8. * @author yangGuang
  9. *
  10. */
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Target(ElementType.METHOD)
  13. public @interface DataSource {
  14. String value();
  15. }

 3.利用Spring的AbstractRoutingDataSource解決多資料源的問題 參考:

​​ javascript:void(0)​​

[html] 

​​view plain​​

​​copy​​

​​

​​

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

    4.利用ThreadLocal解決線程安全問題

[html] 

​​view plain​​

​​copy​​

​​

​​

  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. public static String getDataSource() {
  7. return holder.get();
  8. }
  9. }

    5.定義一個資料源切面類,通過aop通路,在spring配置檔案中配置了,是以沒有使用aop注解。

[html] 

​​view plain​​

​​copy​​

​​

​​

  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. //  @Before(value = "pointCut()")
  14. public void before(JoinPoint point)
  15. {
  16. Object target = point.getTarget();
  17. System.out.println(target.toString());
  18. String method = point.getSignature().getName();
  19. System.out.println(method);
  20. Class<?>[] classz = target.getClass().getInterfaces();
  21. Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
  22. .getMethod().getParameterTypes();
  23. try {
  24. Method m = classz[0].getMethod(method, parameterTypes);
  25. System.out.println(m.getName());
  26. if (m != null && m.isAnnotationPresent(DataSource.class)) {
  27. DataSource data = m.getAnnotation(DataSource.class);
  28. HandleDataSource.putDataSource(data.value());
  29. }
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }

    6.配置applicationContext.xml

[html] 

​​view plain​​

​​copy​​

​​

​​

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

7.使用注解,動态選擇資料源,分别走讀庫和寫庫。

[html] 

​​view plain​​

​​copy​​

​​

​​

  1. @DataSource("write")
  2. public void update(User user) {
  3. userMapper.update(user);
  4. }
  5. @DataSource("read")
  6. public Document getDocById(long id) {
  7. return documentMapper.getById(id);
  8. }