重复提交数据后端处理

203 阅读3分钟

1、Aop  面向切面编程

-通知

1】before

2】After

3】After-returning

4】After-throwing

5】Around

-切点 :定义在切面中的方法,和通知一起使用组成切面

-连接点

-切面 :一般单独作为一个类

-引入

-织入

2、代理模式:

为目标对象创造一个代理对象,以控制对目标对象的访问。

代理模式的应用:权限信息的预处理,token验证;

1】Target:目标对象

2】Proxy :代理对象

3、AOP思想的实现基于代理模式,在java中一般采用JDK 动态代理模式。(仅代理接口)所以无法代理类。

因此,Spring AOP 同时支持CGLIB、ASPECTJ、JDK动态代理,当有实现接口时候,默认使用JDK 动态代理模式,否则采用CGLIB代理。

4、spring 新增注解

注解说明

1】@Target(ElementType.METHOD)  说明了Annotation所修饰的对象范围

2】@Retention 注解用来定义其声明周期

3】@Documented 是一个标记注解

5、实现一个注解类

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface NoRepeat {

    /**

*** 时长

** @return*

*/

**long longTime() default 5000L;

    /**

*** 检查入参

** @return*

*/

**boolean checkBody() default false;

}

6、定义一个切面类 ,进一步实现自定义注解功能

1】 **@Pointcut("@annotation(xxxx)") //** 配置织入点*-xxx*代表自定义注解的存放位置

2@PostConstruct spring容器启动的时候执行,加载常规数据

@Aspect @Component public class NoRepeatAspect { @Autowired StringRedisUtil stringRedisUtil;

RedisDistributedLock redisDistributedLock;

@PostConstruct
private void initRedis() {
    if (redisDistributedLock == null) {
        redisDistributedLock = new RedisDistributedLock(stringRedisUtil.getRedisTemplate());
    }
}

@Pointcut("@annotation(com.cjkj.common.annotation.NoRepeat)") 
public void enterprisePointCut() {

}

@Around("enterprisePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    HttpServletRequest request  = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

// String ip = IPUtil.getIpAddr(request); String token = request.getHeader("Authorization"); LogUtil.info("token:" + token); // 获取注解 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 目标类、方法 String className = method.getDeclaringClass().getName(); String name = method.getName();

    String ipKey = "";
    NoRepeat noRepeat = method.getAnnotation(NoRepeat.class);
    if(noRepeat.checkBody()) {
        Object[] objs = joinPoint.getArgs();
        String body = "";
        if(objs.length>0){
            body = JSONObject.toJSONString(objs[0]);
        }
        ipKey = String.format("%s#%s#%s#%s", token, className, name, body);
    } else {
        ipKey = String.format("%s#%s#%s", token, className, name);
    }
    int hashCode = Math.abs(ipKey.hashCode());
    String key = String.format("%d",hashCode);

// String key = String.format("%s_%d",ip,hashCode); LogUtil.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);

    LogUtil.info("ipKey={},hashCode={}",ipKey,hashCode);
    long timeout = noRepeat.longTime();
    if (timeout < 0){
        //过期时间5分钟
        timeout = 60*5;
    }
    try{
        if (redisDistributedLock.lock("lock:" + key,1,1L)) {
            String value = stringRedisUtil.getStrValue(key);
            if (!StringUtils.isBlank(value)) {
                throw new BusinessException("请勿重复提交");
            }
            stringRedisUtil.set(key, LocalDateTime.now().toString(), timeout / 1000);
        } else {
            throw new BusinessException("请勿重复提交");
        }
    }finally {
        redisDistributedLock.releaseLock("lock:" + key);
    }

    return joinPoint.proceed();
}

}


2、获取注解

3、获取 目标类、方法

4、定义一个ipkey

5、实例化目标对象 NoRepeat

6、创建一个随机数hashCode

7、创建key

8、获取timeout 

9、分布式锁

try{

    *// * @param key         key*

*// * @param expire* 获取锁超时时间

*// * @param retryTimes* 重试次数

*// * @param sleepMillis* 获取锁失败的重试间隔

**if (redisDistributedLock.lock("lock:" + key,1,1L)) 

    { String value = stringRedisUtil.getStrValue(key); *//redis* 存在*key*则 **是重复提交的

**if (!StringUtils.*isBlank*(value)) {

            throw new BusinessException("请勿重复提交");

        } *//* 不存在*key* 则*key*放入*redis +timeout*

**stringRedisUtil.set(key, LocalDateTime.*now*().toString(), timeout / 1000);

    } else {

        throw new BusinessException("请勿重复提交");

    }

}finally {

    redisDistributedLock.releaseLock("lock:" + key);

}

java 锁

synchronized锁 :一次只能允许一个线程进入被锁住的代码块

1、锁的持有者是“线程”,而不是“调用”

2、释放锁

1】当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。

2】当一个线程执行的代码出现异常时,其所持有的锁会自动释放

Lock锁:支持中断、超时不获取、是非阻塞的,必须手动释放锁, 允许多个读线程同时访问共享资源

分布式锁

1、保证多个服务之间互斥的访问共享资源,如果一个服务抢占了分布式锁,其他服务没获取到锁,就不进行后续操作