Site Overlay

自定义Redis事务注解

需求

Redis的multi事务没有找到方便的框架能够自定义提供注解,想要的效果就是在一个方法上添加一个注解以后就可以自动添加redis事务,方法执行失败或者当正在修改的redis数据被修改了,就从头执行一次或者返回错误等之类的操作。实现思路主要是通过自定义注解以及aop环绕通知来实现。

首先写一个注解,用于标识在需要添加事务的方法上。

/**
 * @Author Diuut
 * @Date 2020/6/1  10:51
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface UserRedisTranscational {
  public int NeedTryAgain() default 3;    //默认重试次数
}

然后通过aop绑定该注解。

/**
 * @Author Diuut
 * @Date 2020/6/1  10:49
 */
@Aspect
@Component
@Slf4j
public class UserRedisTransactionalAspect {
    @Autowired
    private UserRedisDao userRedisDao;

    @Around(value = "@annotation(userRedisTransactional)")
    public Object around(ProceedingJoinPoint point, UserRedisTransactional userRedisTransactional) {
        log.info("++++执行了around方法++++");
        //拦截的类名
        Class clazz = point.getTarget().getClass();
        //拦截的方法
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        log.info("执行了 类:" + clazz + " 方法:" + method);
        int tryAgain = userRedisTransactional.tryTimes();    //重试次数
        Object[] args = point.getArgs();
        User user = (User) args[0];
        int uid = user.getUid();
        log.info("uid: {}", uid);
        Object proceed = null;
        try {
            List list = null;
            do {
                if (tryAgain <= 0) {
                    //如果循环次数结束后依旧被锁,就不执行,直接结束,返回值 null
                    return null;
                } else {
                    tryAgain--;
                }
                userRedisDao.watchAndMulti(uid);
                User userByUid = userRedisDao.getUserByUid(uid);
                userByUid.setLastVersion(4);
                args[0] = userByUid;
                log.info("proceed循环中");
                proceed = point.proceed(args);    //执行方法,以最新获取的参数(如果不带args就是用原先的参数)
                list = userRedisDao.exec();
            } while (list.isEmpty());
            log.info("list.proceed循环结束");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
        return proceed;
    }
}

使用注解。环绕通知会从拦截的方法中自动获取到User对象,然后根据调用redistTemplate中的watch以及multi方法,监控这个user的key值,并添加事务。期间可以通过修改args来修改参数。本篇中是在开始监控user之后,再次从redis中获取一次值,以确保在方法执行期间,上锁之前不被修改。

    @UserRedisTransactional(NeedTryAgain = 5)
    public void test32(User user) throws InterruptedException {
        user.setLoginKey("happyPalmTiger");
        userRedisDao.saveUser(user);
    }

发表回复

您的电子邮箱地址不会被公开。

A beliving heart is your magic My heart
欢迎来到Diuut的个人博客,这里是我的一些零零碎碎的知识汇总,希望有能帮到你的内容。 | 蜀ICP备2021011635号-1 | Copyright © 2024 Diuut. All Rights Reserved.