• 技术文章 >Java >java教程

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

    高洛峰高洛峰2017-01-24 10:49:31原创1280

    spring aop , mysql 主从配置 实现读写分离,接下来把自己的配置过程,以及遇到的问题记录下来,方便下次操作,也希望给一些朋友带来帮助。

    1.使用spring aop 拦截机制现数据源的动态选取。

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Target;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    /**
     * RUNTIME
     * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
     * @author yangGuang
     *
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface DataSource {
      String value();
    }

    3.利用Spring的AbstractRoutingDataSource解决多数据源的问题

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
      
     public class ChooseDataSource extends AbstractRoutingDataSource {
      
       @Override
       protected Object determineCurrentLookupKey() {
         return HandleDataSource.getDataSource();
       }
         
     }


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

    public class HandleDataSource {
      public static final ThreadLocal<String> holder = new ThreadLocal<String>();
      public static void putDataSource(String datasource) {
        holder.set(datasource);
      }
        
      public static String getDataSource() {
        return holder.get();
      }  
    }


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

    import java.lang.reflect.Method;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    //@Aspect
    //@Component
    public class DataSourceAspect {
      //@Pointcut("execution(* com.apc.cms.service.*.*(..))") 
      public void pointCut(){}; 
        
     // @Before(value = "pointCut()")
       public void before(JoinPoint point)
        {
          Object target = point.getTarget();
          System.out.println(target.toString());
          String method = point.getSignature().getName();
          System.out.println(method);
          Class<?>[] classz = target.getClass().getInterfaces();
          Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
              .getMethod().getParameterTypes();
          try {
            Method m = classz[0].getMethod(method, parameterTypes);
            System.out.println(m.getName());
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
              DataSource data = m.getAnnotation(DataSource.class);
              HandleDataSource.putDataSource(data.value());
            }
              
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
    }


    6.配置applicationContext.xml

    <!-- 主库数据源 -->
     <bean id="writeDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
      <property name="driverClass" value="com.mysql.jdbc.Driver"/>
      <property name="jdbcUrl" value="jdbc:mysql://172.22.14.6:3306/cpp?autoReconnect=true"/>
      <property name="username" value="root"/>
      <property name="password" value="root"/>
      <property name="partitionCount" value="4"/>
      <property name="releaseHelperThreads" value="3"/>
      <property name="acquireIncrement" value="2"/>
      <property name="maxConnectionsPerPartition" value="40"/>
      <property name="minConnectionsPerPartition" value="20"/>
      <property name="idleMaxAgeInSeconds" value="60"/>
      <property name="idleConnectionTestPeriodInSeconds" value="60"/>
      <property name="poolAvailabilityThreshold" value="5"/>
    </bean>
      
    <!-- 从库数据源 -->
    <bean id="readDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
      <property name="driverClass" value="com.mysql.jdbc.Driver"/>
      <property name="jdbcUrl" value="jdbc:mysql://172.22.14.7:3306/cpp?autoReconnect=true"/>
      <property name="username" value="root"/>
      <property name="password" value="root"/>
      <property name="partitionCount" value="4"/>
      <property name="releaseHelperThreads" value="3"/>
      <property name="acquireIncrement" value="2"/>
      <property name="maxConnectionsPerPartition" value="40"/>
      <property name="minConnectionsPerPartition" value="20"/>
      <property name="idleMaxAgeInSeconds" value="60"/>
      <property name="idleConnectionTestPeriodInSeconds" value="60"/>
      <property name="poolAvailabilityThreshold" value="5"/>
    </bean>
      
    <!-- transaction manager, 事务管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource" />
    </bean>
      
      
    <!-- 注解自动载入 -->
    <context:annotation-config />
      
    <!--enale component scanning (beware that this does not enable mapper scanning!)-->
    <context:component-scan base-package="com.apc.cms.persistence.rdbms" />
    <context:component-scan base-package="com.apc.cms.service">
     <context:include-filter type="annotation" 
        expression="org.springframework.stereotype.Component" /> 
    </context:component-scan> 
      
    <context:component-scan base-package="com.apc.cms.auth" />
      
    <!-- enable transaction demarcation with annotations -->
    <tx:annotation-driven />
      
      
    <!-- define the SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="typeAliasesPackage" value="com.apc.cms.model.domain" />
    </bean>
      
    <!-- scan for mappers and let them be autowired -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="com.apc.cms.persistence" />
      <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
      
    <bean id="dataSource" class="com.apc.cms.utils.ChooseDataSource">
      <property name="targetDataSources"> 
         <map key-type="java.lang.String"> 
           <!-- write -->
           <entry key="write" value-ref="writeDataSource"/> 
           <!-- read -->
           <entry key="read" value-ref="readDataSource"/> 
         </map> 
           
      </property> 
      <property name="defaultTargetDataSource" ref="writeDataSource"/> 
    </bean>
       
    <!-- 激活自动代理功能 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
      
    <!-- 配置数据库注解aop -->
    <bean id="dataSourceAspect" class="com.apc.cms.utils.DataSourceAspect" />
    <aop:config>
      <aop:aspect id="c" ref="dataSourceAspect">
        <aop:pointcut id="tx" expression="execution(* com.apc.cms.service..*.*(..))"/>
        <aop:before pointcut-ref="tx" method="before"/>
      </aop:aspect>
    </aop:config>
    <!-- 配置数据库注解aop -->

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

    @DataSource("write")
    public void update(User user) {
      userMapper.update(user);
    }
      
    @DataSource("read")
    public Document getDocById(long id) {
      return documentMapper.getById(id);
    }

    测试写操作:可以通过应用修改数据,修改主库数据,发现从库的数据被同步更新了,所以定义的write操作都是走的写库

    测试读操作: 后台修改从库数据,查看主库的数据没有被修改,在应用页面中刷新,发现读的是从库的数据,说明读写分离ok。

    遇到的问题总结:

    问题1:项目是maven工程,用到了Spring aop机制,除了spring的核心jar包以为,还需要用到的jar包有aspectj.jar,aspectjweaver.jar,aopalliance.jar查看项目中的pom,发现缺少依赖包,由于本地仓库没有这些jar,查找可以提供下载jar包的maven中央库库,配置到maven中,自动更新:

    <repository>
       <id>nexus</id>
       <name>nexus</name>
       <url>http://repository.sonatype.org/content/groups/public/</url>
       <layout>default</layout>
     </repository>


    配置项目依赖的jar,主要是缺少这两个。

      <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.5.4</version>
    </dependency>
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.5.4</version>
    lt;/dependency>


    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。

    更多详解使用spring aop实现业务层mysql 读写分离相关文章请关注PHP中文网!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:Spring实现动态切换多数据源的解决方案 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 一起来分析java设计模式之单例• Java数据结构之单链表与OJ题• 一文搞懂Java线程池实现原理• Java总结分享之反射、枚举、Lambda表达式• 详细介绍Java正则表达式之单字符匹配和预定义字符
    1/1

    PHP中文网