Spring-aop /@Around增强/防止表单一分钟内多次提交

1 前言

        平时我们在开发中,可能会对一些功能进行增强处理。比如对于某一类中所有的方法或者某一类中特定的方法进行增强。此时我们就可能会想到用到SpringAop中的@Aroung环绕功能。

在此篇文章中,我们用定义注解+@Around防止接口一分钟内被多次调用来梳理这个功能。

2 @Around

@Around的作用:

1 可以在目标方法之前进行织入操作,也可以在执行目标方法之后织入增强动作

2 可以决定目标方法在什么时候执行,如何执行,甚至可以阻止目标方法的执行

3 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值;当需要改变目标方法时,只能使用around方法

@Around环绕通知,为什么被称为切面中功能最为强大的通知类型呢,因为它既可以实现@Before通知的功能(将增强功能写在proceedingJoinPoint.proceed()方法之前),也可以实现@After通知的功能(将增强功能写在proceedingJoinPoint.proceed()方法之后)。它也可以同时在拦截方法之前和拦截方法之后同时进行增强(增强功能>>>proceedingJoinPoint.proceed()>>>增强功能)

3 代码演示

步骤一:自定义注解

@Target({ElementType.METHOD})   //作用于方法上
@Retention(RetentionPolicy.RUNTIME)  //运行期有效
public @interface RepeatAnnotation {
}

步骤二:自定义切面

@Aspect
@Component
@Slf4j
public class AroundAspect {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    private static String REPEAT_KEY = "repeatKey";

    /**
     * 注意: ProceedingJoinPoint只能用于环绕通知,因为ProceedingJoinPoint暴露了proceed()方法
     * @param joinPoint
     * @param repeatAnnotation
     * @return
     */
    @Around(value = "@annotation(repeatAnnotation)")
    public Object annotationAround(ProceedingJoinPoint joinPoint, RepeatAnnotation repeatAnnotation) throws JsonProcessingException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        log.info("打印请求url:{}", String.format("%s:%s%s", request.getRemoteHost(), request.getRemotePort(), request.getRequestURI()));
        //类名称
        String className = joinPoint.getTarget().getClass().toString();
        //方法名称
        String name = joinPoint.getSignature().getName();
        //参数值
        Object[] args = joinPoint.getArgs();
        ObjectMapper objectMapper = new ObjectMapper();
        log.info("调用前:"+name+"方法参数"+objectMapper.writeValueAsString(args));
        //设置一个防重检查key-value{repeatKey:1}
        Boolean result = redisTemplate.opsForValue().setIfAbsent(REPEAT_KEY, "张三");
        if (result == false){
            return "该请求一分钟只能发送一次";
        }
        redisTemplate.expire(REPEAT_KEY,60, TimeUnit.SECONDS);
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("获取目标方法异常",throwable);
        }
        log.info("调用后:"+name+"返回结果"+objectMapper.writeValueAsString(proceed));
        return proceed;

    }
}

步骤三:Redis配置

public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();

        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式

        template.setKeySerializer(stringRedisSerializer);

        // hash的key也采用String的序列化方式

        template.setHashKeySerializer(stringRedisSerializer);

        // value序列化方式采用jackson

        template.setValueSerializer(jackson2JsonRedisSerializer);

        // hash的value序列化方式采用jackson

        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();

        return template;

    }
}

步骤四:Redis配置文件

spring:
  redis:
    database: 0 #Redis数据库索引 默认是0
    host: 47.118.53.22  #redis服务器地址
    port: 6379 #redis连接端口
    password:       #redis连接密码默认没有
    jedis:
      pool:
        max-wait: -1ms  #连接池最大阻塞时间 使用负值表示没有限制
        max-active: 8   #连接池最大连接数 使用负值表示没有限制
        max-idle: 8     #连接池中的最大空闲连接
        min-idle: 0     #连接池中的最小空闲连接
    connect-timeout: 5000 #连接超时时间

步骤五:访问接口

@RestController
@RequestMapping("/test")
public class Around {


    @GetMapping("/around")
    @RepeatAnnotation
    public String testAround(String name){
        System.out.println("一分钟之后>>>>"+name+"进来了");
        return name+"退出了该方法";

    }
}

在一分钟内连续访问两次结果:

posted @ 2021-12-12 19:05  小猪不会叫  阅读(77)  评论(0)    收藏  举报  来源