`

使用spring aop实现业务层mysql 读写分离

    博客分类:
  • Java
 
阅读更多

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>  
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics