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

spring aop , mysql 主從配置 實現讀寫分離,下來把自己的配置過程,以及遇到的問題記錄下來,方便下次操作,也希望給一些朋友帶來幫助。
mysql主從配置參看:http://blog.csdn.net/huoyunshen88/article/details/26597483

1.使用spring aop 攔截機制現數據源的動態選取。

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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解決多數據源的問題 參考: http://blog.csdn.net/alaahong/article/details/8707915

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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.        
  10.  }  



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

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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註解。

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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.使用註解,動態選擇數據源,分別走讀庫和寫庫。

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  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。

 
遇到的問題總結:

  問題1:項目是maven工程,用到了Spring aop機制,除了spring的核心jar包以爲,還需要用到的jar包有aspectj.jar,aspectjweaver.jar,aopalliance.jar查看項目中的pom,發現缺少依賴包,
    由於本地倉庫沒有這些jar,查找可以提供下載jar包的maven中央庫庫,配置到maven中,自動更新:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  1. <repository>  
  2.      <id>nexus</id>  
  3.      <name>nexus</name>  
  4.      <url>http://repository.sonatype.org/content/groups/public/</url>  
  5.      <layout>default</layout>  
  6.  </repository>  


    配置項目依賴的jar,主要是缺少這兩個。

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
 
  1.    <dependency>  
  2.        <groupId>aspectj</groupId>  
  3.        <artifactId>aspectjrt</artifactId>  
  4.        <version>1.5.4</version>  
  5. </dependency>  
  6. <dependency>  
  7.        <groupId>aspectj</groupId>  
  8.        <artifactId>aspectjweaver</artifactId>  
  9.        <version>1.5.4</version>  
  10. lt;/dependency>  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章