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

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> 

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

(0)

相关推荐

  • SpringBoot集成Spring Data JPA及读写分离

    相关代码: github OSCchina JPA是什么 JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具 来管理Java应用中的关系数据.它包括以下几方面的内容: 1.ORM映射 支持xml和注解方式建立实体与表之间的映射. 2.Java持久化API 定义了一些常用的CRUD接口,我们只需直接调用,而不需要考虑底层JDBC和SQL的细节. 3.JPQL查询语言 这是持久化操作中很重要的一个方面,通过面向对象

  • Spring动态数据源实现读写分离详解

    一.创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性 package com.bounter.mybatis.extension; /** * 基于ThreadLocal实现的动态数据源容器,保证DynamicDataSource的线程安全性 * @author simon * */ public class DynamicDataSourceHolder { private static final ThreadLocal<String> dataSourceHolde

  • 详解Spring AOP 实现主从读写分离

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写分离」并不是多么神奇的东西,也带不来多么大的性能提升,也许更多的作用的就是数据安全的备份吧. 从一个库到读写分离,从理论上对服务器压力来说是会带来一倍的性能提升,但你仔细思考一下,你的应用服务器真的很需要这一倍的提升么?那倒不如你去试着在服务器使用一下缓存系统,如 Memcached.Redis 这

  • SpringMVC4+MyBatis+SQL Server2014实现数据库读写分离

    前言 基于mybatis的AbstractRoutingDataSource和Interceptor用拦截器的方式实现读写分离,根据MappedStatement的boundsql,查询sql的select.insert.update.delete,根据起判断使用读写连接串. 开发环境 SpringMVC4.mybatis3 项目结构 读写分离实现 1.pom.xml <dependencies> <dependency> <groupId>junit</grou

  • Spring boot实现数据库读写分离的方法

    背景 数据库配置主从之后,如何在代码层面实现读写分离? 用户自定义设置数据库路由 Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库. 实现可动态路由的数据源,在每次数据库查询操作前执行 ReadWriteSplitRoutingDataSource.java import org.springframework.jdbc.datasource.lookup.Abst

  • Spring 实现数据库读写分离的示例

    现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力. 采用读写分离技术的目标:有效减轻Master库的压力,又可以

  • Spring配置动态数据源实现读写分离的方法

    前言 最近因为工作的需要,要搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,有需要的朋友们可以参考学习. 首先是配置数据源 <!--读数据源配置--> <bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"destroy-method="close"> //配置省

  • Spring+MyBatis实现数据库读写分离方案

    推荐第四种 方案1 通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactoryBean对象的mapperLocations属性制定两个读写数据源的配置文件.将所有读的操作配置在读文件中,所有写的操作配置在写文件中. 优点:实现简单 缺点:维护麻烦,需要对原有的xml文件进行重新修改,不支持多读,不易扩展 实现方式 <bean id="abstractDataSource" abstract="true" class=

  • 使用Spring AOP实现MySQL数据库读写分离案例分析(附demo)

     一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离

  • Spring AOP切面解决数据库读写分离实例详解

    Spring AOP切面解决数据库读写分离实例详解 为了减轻数据库的压力,一般会使用数据库主从(master/slave)的方式,但是这种方式会给应用程序带来一定的麻烦,比如说,应用程序如何做到把数据写到master库,而读取数据的时候,从slave库读取.如果应用程序判断失误,把数据写入到slave库,会给系统造成致命的打击. 解决读写分离的方案很多,常用的有SQL解析.动态设置数据源.SQL解析主要是通过分析sql语句是insert/select/update/delete中的哪一种,从而对

随机推荐