原文链接: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 #连接超时时间
步骤五:访问接口
以上只是部分内容,为了维护方便,本文已迁移到新地址:Spring-aop /@Around增强/防止表单一分钟内多次提交 – 编程屋