防重

作者: Ian | 2019-03-31 | 阅读
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheLock {
    /**
     * redis 锁key的前缀
     *
     * @return redis 锁key的前缀
     */
    String prefix() default "";

    /**
     * 过期秒数,默认为10秒
     *
     * @return 轮询锁的时间
     */
    int expire() default 10;

    /**
     * 超时时间单位
     *
     * @return 秒
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

    /**
     * <p>Key的分隔符(默认 :)</p>
     * <p>生成的Key:N:SO1008:500</p>
     *
     * @return String
     */
    String delimiter() default "_";

}

import com.i78dk.appserver.common.annotation.CacheLock;
import com.i78dk.appserver.common.annotation.CacheParam;
import com.i78dk.appserver.common.constants.BusiConstants;
import com.i78dk.appserver.common.context.UserContext;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class LockKeyGeneratorUtil {
    public static String getLockKey(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        CacheLock lockAnnotation = method.getAnnotation(CacheLock.class);
        final Object[] args = pjp.getArgs();
        final Parameter[] parameters = method.getParameters();
        StringBuilder builder = new StringBuilder();
        StringBuilder prefix = new StringBuilder(BusiConstants.PREVENT_REPEAT_PREFIX+lockAnnotation.prefix());
        //获取当前线程缓存的手机号
        Object mobile = UserContext.get().getMobile();
        //未指定前缀使用方法名作为前缀
         if(BusiConstants.PREVENT_REPEAT_PREFIX.equals(prefix.toString())){
             prefix.append(method.getName())
                     .append(lockAnnotation.delimiter());
         }
        if(mobile != null){
            prefix.append(mobile.toString());
        }
        //默认解析方法里面带 CacheParam 注解的属性,如果没有尝试着解析实体对象中的
        for (int i = 0; i < parameters.length; i++) {
            final CacheParam annotation = parameters[i].getAnnotation(CacheParam.class);
            if (annotation == null) {
                continue;
            }
            builder.append(lockAnnotation.delimiter()).append(args[i]);
        }
        if (StringUtils.isEmpty(builder.toString())) {
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    final CacheParam annotation = field.getAnnotation(CacheParam.class);
                    if (annotation == null) {
                        continue;
                    }
                    field.setAccessible(true);
                    builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object));
                }
            }
        }
        return prefix.toString() + builder.toString();
    }
}

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheParam {
    /**
     * 字段名称
     *
     * @return String
     */
    String name() default "";
}

import com.i78dk.appserver.common.annotation.CacheLock;
import com.i78dk.appserver.common.constants.ResultCodeConstants;
import com.i78dk.appserver.common.exception.AssertException;
import com.i78dk.appserver.common.redis.CacheRedisService;
import com.i78dk.appserver.common.util.LockKeyGeneratorUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
public class ServiceLogAspect {
    @Autowired
    private CacheRedisService cacheRedisService;
    private Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    private ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    @Pointcut("execution(public * com.i78dk.*.service..*.*(..))")
    public void serviceLog() {}

    @Before("serviceLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 请求内容
        logger.info("[controller]----CLASS_METHOD: " + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName() + ",参数 : "
                + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "ret", pointcut = "serviceLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 返回内容
        logger.info("[controller]----返回值 : " + ret);
    }

    @Around("serviceLog()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        startTime.set(System.currentTimeMillis());
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        CacheLock key = method.getAnnotation(CacheLock.class);
        String lockKey = "lock key is empty";
        //防重复提交
        if (key != null) {
            lockKey = LockKeyGeneratorUtil.getLockKey(pjp);
            boolean b = cacheRedisService.setIfAbsent(lockKey, true, key.expire());
            if (!b) {
                throw new AssertException(ResultCodeConstants.CODE_S0025);
            }
        }
        try {
            // ob 为方法的返回值
            Object ob = pjp.proceed();
            logger.info("[controller]----耗时 : " + (System.currentTimeMillis() - startTime.get()));
            return ob;
        }finally {
            //无论业务是否成功释放锁
            if(key != null){
                cacheRedisService.remove(lockKey);
            }
        }


    }
}

    /**
     * 设置有效时间
     * @param key
     * @param expire
     * @return
     */
    @Override
    public boolean expire(final String key, long expire) {
        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }


    @Override
    public boolean setIfAbsent(String key, Object o, long expire) {
        boolean sucess = false;
        try {
          sucess = redisTemplate.opsForValue().setIfAbsent(key, o);
          //保存成功设置过期时间
          if(sucess){
              expire(key, expire);
          }
        }catch (Exception e){
            e.printStackTrace();
            return sucess;
        }
        return sucess;
    }

文章中若有不足之处,还望指出。坚持是一种精神,分享是一种快乐!


版权声明:本文由 Ian 在 2019年03月31日发表。本文采用CC BY-NC-SA 4.0许可协议,非商业转载请注明出处,不得用于商业目的。
文章题目及链接:《防重》




  相关文章:


留言区:

TOP