aop实现乐观锁结合redis

说明

本项目需要对业务实现乐观锁,防止数据重复操作,结合redis实现。本文为代码实现,无其他说明,学习aop,redis,乐观锁请自行学习。

1、业务处理后置加锁

自定义注解

package com.e6yun.project.tms.revoke.annotation;

import com.e6yun.project.tms.revoke.common.ParamType;

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 AfterDistributeHandle {

    /**
     * 唯一标识字段名
     * 实体类参数名
     * @return
     */
    public String columnName();

    /**
     * 基本数据类型使用single
     * 对象使用 mutile
     * @return
     */
    public ParamType type() default ParamType.SINGLE;

}

切面

package com.e6yun.project.tms.revoke.aspect;

import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.annotation.AfterDistributeHandle;
import jodd.util.StringUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Lazy(false)
public class AfterDistributeAspect {

    Logger logger = LoggerFactory.getLogger(AfterDistributeAspect.class);

    final String status = "Running";

    @Resource
    @Qualifier("reportRedis")
    private E6RedisService redisService;

    @Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.AfterDistributeHandle)")
    private void cutMethod(){
    }

    /**
     * 后置处理
     * @param joinPoint
     * @throws Throwable
     */
    @AfterReturning("cutMethod()")
    public void after(JoinPoint joinPoint) throws Throwable {

        logger.info(">>>>>>>>>>>>>>>>加锁<<<<<<<<<<<<<<<<<<<<<");

        // 获取方法传入参数
        String orderNum = getColumnValue(joinPoint);
        logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);

        if (StringUtil.isNotBlank(orderNum)){
            try {
                //根据从redis获取对应value
                redisService.setValue(orderNum, status, 60, TimeUnit.SECONDS);
            }catch (Exception e){
                redisService.delete(orderNum);
                logger.info(">>>>>>>>>>>>>>redis加锁失败{},参数:{}",e ,orderNum);
            }
        }
    }

    /**
     * 后置异常处理
     * @param joinPoint
     * @throws Throwable
     */
    @AfterThrowing(pointcut = "cutMethod()")
    public void afterThrow(JoinPoint joinPoint) throws Throwable {

        logger.info(">>>>>>>>>>>>>>>>异常去锁<<<<<<<<<<<<<<<<<<<<<");

        // 获取方法传入参数
        String orderNum = getColumnValue(joinPoint);
        logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);

        if (StringUtil.isNotBlank(orderNum)){
            try {
                //从redis删除对应value
                redisService.delete(orderNum);
            }catch (Exception e){
                logger.info(">>>>>>>>>>>>>>redis加锁失败{},参数:{}",e ,orderNum);
            }
        }
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    private static AfterDistributeHandle getDeclaredAnnotation(JoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        AfterDistributeHandle annotation = objMethod.getDeclaredAnnotation(AfterDistributeHandle.class);
        // 返回
        return annotation;
    }

    /**
     * 根据注解设置获取对象列参数值
     * @param joinPoint
     * @return
     */
    protected static String getColumnValue(JoinPoint joinPoint){
        AfterDistributeHandle handle = null;
        Object[] params = joinPoint.getArgs();
        try {
            handle = getDeclaredAnnotation(joinPoint);
            for (Object param : params) {

                // 修改为参数判断,单参数和对象
                handle.type().value();
                if (handle.type().value() == ParamType.SINGLE.value()){
                    //根据列明 获取单参数信息
                    return String.valueOf(param);
                }

                if (handle.type().value() == ParamType.MUTIL.value()){
                    Field[] fields = param.getClass().getDeclaredFields();

                    for (Field field : fields) {
                        // 私有属性必须设置访问权限
                        field.setAccessible(true);
                        String name = field.getName();
                        if (name.equals(handle.columnName())){
                            Object a = field.get(param);
                            System.out.println(a);
                            return String.valueOf(a);
                        }else {
                            continue;
                        }
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

2、并发业务进入

注解

package com.e6yun.project.tms.revoke.annotation;

import com.e6yun.project.tms.revoke.common.ParamType;

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

/**
 * 返回类型为E6Wrapper
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RevokeHandle {

    /**
     * 唯一标识字段名
     * 实体类参数名
     * @return
     */
    public String columnName();

    /**
     * 基本数据类型 使用single
     * 入参为 对象 使用 mutile
     * @return
     */
    public ParamType type() default ParamType.SINGLE;

}

并发前置处理,判断是否加锁

package com.e6yun.project.tms.revoke.aspect;

import com.alibaba.fastjson.JSON;
import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.common.vo.E6Wrapper;
import com.e6yun.project.tms.revoke.annotation.CleanCacheHandle;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.utils.AspectUtil;
import jodd.util.StringUtil;
import net.bytebuddy.implementation.bytecode.Throw;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.rmi.server.ExportException;

@Aspect
@Component
@Lazy(false)
public class RevokeAspect {
    Logger logger = LoggerFactory.getLogger(RevokeAspect.class);

    final String status = "Running";

    @Resource
    @Qualifier("reportRedis")
    private E6RedisService redisService;

    @Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.RevokeHandle)")
    private void cutMethod(){
    }

    /**
     * 前置通知:在目标方法执行前调用
     */
    @Around("cutMethod()")
    public Object begin(ProceedingJoinPoint joinPoint) throws Throwable {

        logger.info(">>>>>>>>>>>>>>>>撤销加锁判断<<<<<<<<<<<<<<<<<<<<<");

        // 获取方法传入参数
        String orderNum = getColumnValue(joinPoint);
        logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);

        if (StringUtil.isNotBlank(orderNum)){
            try {
                //根据num从redis获取对应value
                String catchInfo = redisService.getValue(orderNum);

                //判断当前加锁情况
                if (StringUtil.isNotBlank(catchInfo) && catchInfo.equals(status)){
                    //已被锁定,直接返回禁止操作
                    E6Wrapper wrapper = new E6Wrapper();
                    wrapper.setCode(E6Wrapper.CODE_NO_PERMISSION);
                    wrapper.setMessage("当前数据正在处理,请稍后再试");
                    return wrapper;
                }
            }catch (Exception e){
                logger.info(">>>>>>>>>>>>>>获取redis缓存失败:{}",e);
            }
        }

        //未被锁定,允许操作
        return joinPoint.proceed();
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    private static RevokeHandle getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Method objMethod = AspectUtil.getMethod(joinPoint);
        // 拿到方法定义的注解信息
        RevokeHandle annotation = objMethod.getDeclaredAnnotation(RevokeHandle.class);
        // 返回
        return annotation;
    }

    /**
     * 根据注解设置获取对象列参数值
     * @param joinPoint
     * @return
     */
    public static String getColumnValue(ProceedingJoinPoint joinPoint){
        RevokeHandle handle = null;
        Object[] params = joinPoint.getArgs();
        try {
            handle = getDeclaredAnnotation(joinPoint);
            for (Object param : params) {

                // 修改为参数判断,单参数和对象
                handle.type().value();
                if (handle.type().value() == ParamType.SINGLE.value()){
                    //根据列明 获取单参数信息
                    return String.valueOf(param);
                }

                if (handle.type().value() == ParamType.MUTIL.value()){
                    Field[] fields = param.getClass().getDeclaredFields();

                    for (Field field : fields) {
                        // 私有属性必须设置访问权限
                        field.setAccessible(true);
                        String name = field.getName();
                        if (name.equals(handle.columnName())){
                            Object a = field.get(param);
                            System.out.println(a);
                            return String.valueOf(a);
                        }else {
                            continue;
                        }
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

3、业务完成取消加锁

注解

package com.e6yun.project.tms.revoke.annotation;

import com.e6yun.project.tms.revoke.common.ParamType;

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

/**
 * 返回类型为E6Wrapper
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CleanCacheHandle {

    /**
     * 唯一标识字段名
     * 实体类参数名
     * @return
     */
    public String columnName();

    /**
     * 基本数据类型 使用single
     * 入参为 对象 使用 mutile
     * @return
     */
    public ParamType type() default ParamType.SINGLE;

}

切面实现,删除redis缓存

package com.e6yun.project.tms.revoke.aspect;

import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.common.vo.E6Wrapper;
import com.e6yun.project.tms.revoke.annotation.CleanCacheHandle;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.utils.AspectUtil;
import jodd.util.StringUtil;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Aspect
@Component
@Lazy(false)
public class CleanCacheAspect {
    Logger logger = LoggerFactory.getLogger(CleanCacheAspect.class);

    final String status = "Running";

    @Resource
    @Qualifier("reportRedis")
    private E6RedisService redisService;

    @Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.CleanCacheHandle)")
    private void cutMethod(){
    }

    /**
     * 前置通知:在目标方法执行前调用
     */
    @Around("cutMethod()")
    public Object begin(ProceedingJoinPoint joinPoint) throws Throwable {

        logger.info(">>>>>>>>>>>>>>>>删除加锁缓存<<<<<<<<<<<<<<<<<<<<<");

        // 获取方法传入参数
        String orderNum = getColumnValue(joinPoint);
        logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);

        if (StringUtil.isNotBlank(orderNum)){
            try {
                //根据num从redis获取对应value
                redisService.delete(orderNum);
            }catch (Exception e){
                logger.info(">>>>>>>>>>>>>>删除redis缓存失败:{}",e);
            }
        }

        //未被锁定,允许操作
        return joinPoint.proceed();
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    private static CleanCacheHandle getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Method objMethod = AspectUtil.getMethod(joinPoint);
        // 拿到方法定义的注解信息
        CleanCacheHandle annotation = objMethod.getDeclaredAnnotation(CleanCacheHandle.class);
        // 返回
        return annotation;
    }

    /**
     * 根据注解设置获取对象列参数值
     * @param joinPoint
     * @return
     */
    public static String getColumnValue(ProceedingJoinPoint joinPoint){
        CleanCacheHandle handle = null;
        Object[] params = joinPoint.getArgs();
        try {
            handle = getDeclaredAnnotation(joinPoint);
            for (Object param : params) {

                // 修改为参数判断,单参数和对象
                handle.type().value();
                if (handle.type().value() == ParamType.SINGLE.value()){
                    //根据列明 获取单参数信息
                    return String.valueOf(param);
                }

                if (handle.type().value() == ParamType.MUTIL.value()){
                    Field[] fields = param.getClass().getDeclaredFields();

                    for (Field field : fields) {
                        // 私有属性必须设置访问权限
                        field.setAccessible(true);
                        String name = field.getName();
                        if (name.equals(handle.columnName())){
                            Object a = field.get(param);
                            System.out.println(a);
                            return String.valueOf(a);
                        }else {
                            continue;
                        }
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

公共util

package com.e6yun.project.tms.revoke.utils;

import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class AspectUtil {

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    public static Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        return objMethod;
    }

}

注:其中redis服务请自行配置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da297368860

你的鼓励是优质内容产出的最大动

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值