使用切面AOP实现全局乐观锁

最近项目需要版本号乐观锁,但发现每个需要加锁的地方都要做处理发现很繁琐很臃肿,所以使用aop切面+自定义注解来抽取实现乐观锁。

第一步使用@Aspect需要pom引入

<!--使用AspectJ方式注解需要相应的包-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.1</version>
</dependency>

第二步: 自定义注解

package com.aop;

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

/**
 * 自定义尝试切面接口
 */
// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
//@interface定义一个注解
public @interface IsTryAgain {
}

第三步: 自定义一个异常 ApiException

第四部:编写AOP

package com.aop;

import com.aop.exception.ApiException;
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.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 定义重试切面方法,是为了发生乐观锁异常时在一个全新的事务里提交上一次的操作,
 * 直到达到重试上限;因此切面实现 org.springframework.core.Ordered 接口,
 * 这样我们就可以把切面的优先级设定为高于事务通知 。
 */
@Aspect
@Component
public class ConcurrentOperationExecutor implements Ordered {
    private int maxRetries = 3;
    private int order = 1;

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

    public int getOrder() {
        return this.order;
    }

    /**
     * 切点可访问性修饰符与类可访问性修饰符的功能是相同的,它可以决定定义的切点可以在哪些类中可使用。
     * 因为命名切点仅利用方法名及访问修饰符的信息,所以我们一般定义方法的返回类型为 void ,并且方法体为空 。
     *
     * 所有加了自定义注解的地方切入
     *
     */
    @Pointcut("@annotation(IsTryAgain)")
    public void businessService() {

    }


    @Around("businessService()")
    @Transactional(rollbackFor = Exception.class)
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            } catch (TryAgainException ex) {
                if (numAttempts > maxRetries) {
                    throw new ApiException(1, "重试达到最大次数");
                } else {
                    System.out.println("=====try recovery=====");
                }
            }
        } while (numAttempts <= this.maxRetries);

        return null;
    }
}

方法类:

/**
 * update有排它锁,当当前事务update没提交之前,另外集群上面的事务不能更新需要等待
 * @param content
 * @throws Exception
 */
@IsTryAgain
@Transactional(rollbackFor = Exception.class)
public void IsTryAgainMethod(String content) throws Exception{
    HappyLockTest happyLockTest = happyLockTestMapper.selectByPrimaryKey(1L);
    happyLockTest.setContent(content);
    System.out.println(content+"--->"+happyLockTest.getVersion());
    Thread.sleep(8000);
    System.out.println("开始--->");
    happyLockTest.setVersion(happyLockTest.getVersion());
    int temp =  happyLockTestMapper.updateByPrimaryKeySelective(happyLockTest);
    if (temp == 0){
        throw new TryAgainException("重试");
    }
    System.out.println("结束了去查询吧------------》");
    Thread.sleep(8000);
    System.out.println("---------------结束------------");
}

sql:

<update id="updateByPrimaryKeySelective" parameterType="com.pojo.HappyLockTest" >
  update happy_lock_test
  <set >
    <if test="content != null" >
      content = #{content,jdbcType=VARCHAR},
    </if>
    <if test="version != null" >
      version = #{version,jdbcType=INTEGER} + 1,
    </if>
  </set>
  where id = #{id,jdbcType=BIGINT} and version = #{version}
</update>

参考网友:https://blog.csdn.net/zhanghongzheng3213/article/details/50819539

猜你喜欢

转载自blog.csdn.net/Java_Mrsun/article/details/85710376
今日推荐