Spring AOP解决乐观锁重试 - 简书

标签: | 发表时间:2020-09-14 23:57 | 作者:
出处:https://www.jianshu.com

最近在阅读《阿里巴巴Java开发手册》时有这么一段内容:

【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。

那这个重试机制怎么实现呢,大体思路是:

用aop解决问题
1.自定义重试注解及切面
2.在需要重试的接口上加上自定义注解
3.自定义异常
4.更新失败/业务异常 的时候抛出自定义异常
5.切面中定义重试机制

代码:

1.自定义重试注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

  • 重试注解(异常重试,乐观锁更新失败重试)
    */
    @Target(ElementType.METHOD) // 作用到方法上
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NeedTryAgain {

    /**

    • 重试次数
    • @return
      */
      int tryTimes() default 3;

}
2.在需要重试的service接口上增加注解

/**

  • 修改用户
  • @return
    */
    @NeedTryAgain(tryTimes = 3)
    RbacUser update(RbacUserDTO user);
    3.自定义异常

import org.springframework.http.HttpStatus;

import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;

/**

  • 自定义重试异常
    */
    public class TryAgainException extends RuntimeException {

    private Integer status = INTERNAL_SERVER_ERROR.value();

    public TryAgainException( String msg) {
    super(msg);
    }

    public TryAgainException(HttpStatus status, String msg) {
    super(msg);
    this.status = status.value();
    }

}
4.更新失败抛出自定义异常

...

if(1 != rbacUserMapper.update(rbacUser)){
// 回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 保存失败,抛出重试异常
throw new TryAgainException("乐观锁导致修改失败");
}

...
5.自定义重试切面及重试机制

import com.gourd.base.annotation.NeedTryAgain;
import com.gourd.base.exception.BadRequestException;
import com.gourd.base.exception.TryAgainException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

/**

  • 重试切面

  • @author gourd
    */
    @Aspect
    @Component
    @Slf4j
    public class TryAgainAspect{

    private int maxRetries;

    public void setMaxRetries(int maxRetries) {
    this.maxRetries = maxRetries;
    }

    @Pointcut("@annotation(com.gourd.base.annotation.NeedTryAgain)")
    public void retryOnOptFailure() {
    }

    @Around("retryOnOptFailure()")
    @Transactional(rollbackFor = Exception.class)
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
    // 获取拦截的方法名
    MethodSignature msig = (MethodSignature) pjp.getSignature();
    // 返回被织入增加处理目标对象
    Object target = pjp.getTarget();
    // 为了获取注解信息
    Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
    // 获取注解信息
    NeedTryAgain annotation = currentMethod.getAnnotation(NeedTryAgain.class);
    // 设置重试次数
    this.setMaxRetries(annotation.tryTimes());
    // 重试次数
    int numAttempts = 0;
    do {
    numAttempts++;
    try {
    // 再次执行业务代码
    return pjp.proceed();
    } catch (TryAgainException ex) {
    if (numAttempts > maxRetries) {
    // 如果大于 默认的重试机制 次数,我们这回就真正的抛出去了
    throw new BadRequestException(HttpStatus.BAD_REQUEST,"服务内部错误-重试结束");
    }else{
    // 如果 没达到最大的重试次数,将再次执行
    log.info(" 0 === 正在重试====="+numAttempts+"次");
    }
    }
    } while (numAttempts <= this.maxRetries);

            return null;
    

    }
    }
    至此重试机制就完成了,如果是JPA或者hibernate,乐观锁保存失败会报异常,同样的try..catch(ObjectOptimisticLockingFailureException)到之后再抛出自定义异常。




相关 [spring aop 乐观锁] 推荐:

Spring AOP解决乐观锁重试 - 简书

- -
最近在阅读《阿里巴巴Java开发手册》时有这么一段内容:. 【强制】并发修改同一记录时,避免更新丢失,需要加锁. 要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据. 说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁. 那这个重试机制怎么实现呢,大体思路是:.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.

Spring AOP监控SQL执行

- - CSDN博客架构设计推荐文章
         对数据库连接池Proxool比较熟悉的读者,都知道Proxool可以记录SQL执行内容和时间等信息日志. 我们可以将该日志记录专门的SQL日志文件,对于查找执行特别耗时的SQL起了不小的作用. 对于一些其他连接池,没有该特性时,本文介绍Spring AOP切面方法来记录SQL日志.

Spring AOP 实现原理与 CGLIB 应用

- - 博客 - 伯乐在线
来源: IBM Developerworks. 简介: AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等.

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

使用spring AOP获得session的思路

- - RSS - IT博客云
由于Spring 的AOP面向切面编程,与Servlet容器没有任何关联,所以想要获得Session会话比较麻烦. 当然Struts2同样不依赖Servlet容器,可以在Spring AOP中可以使用 com.opensymphony.xwork2.ActionContext,就可以获得 Session.

Spring AOP + Redis缓存数据库查询

- - 编程语言 - ITeye博客
我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 必须要做到与业务逻辑代码完全分离. 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识. Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个.

Spring AOP动态代理原理与实现方式 (转)

- - 开源软件 - ITeye博客
AOP:面向切面、面向方面、面向接口是一种横切技术. 1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   . 3.安全验证: Spring AOP---OOP升级  . 静态代理原理:目标对象:调用业务逻辑    代理对象:日志管理. 表示层调用--->代理对象(日志管理)-->调用目标对象.

基于 Annotation 拦截的 Spring AOP 权限验证方法

- - 企业架构 - ITeye博客
转自: http://www.ibm.com/developerworks/cn/java/j-lo-springaopfilter/index.html. 使用 Annotation 可以非常方便的根据用户的不同角色,分配访问 Java 方法的权限. 在 Java Web 开发中,使用这种方法,可以提高系统的松耦合度,方便维护.

Spring aop 原理及各种应用场景

- - 开源软件 - ITeye博客
AOP是Aspect Oriented Programing的简称,面向切面编程. AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录. AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强.