开发环境:SpringBoot + JDK8
1. 自定义 @NoRepeatForm 注解
/**
* 说明:
* 1.RetentionPolicy.RUNTIME 运行时生效
* 2.ElementType.METHOD 作用在方法级别
* 3.请求未完成时,默认2秒内不可重复提交
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoRepeatForm {
long timeout() default 2000; //超时时间
}
2. 实现HandlerInterceptor,自定义拦截器
@Component //注入Spring进行管理
public class FormInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final Logger LOGGER = LoggerFactory.getLogger(FormInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
Method method = ((HandlerMethod) handler).getMethod();
NoRepeatForm noRepeatForm = method.getAnnotation(NoRepeatForm.class);
if (noRepeatForm == null) { //如果该方法未标记注解,不作任何处理
return true;
}
String key=getKey(request,method); //获取唯一Key
Object obj = stringRedisTemplate.opsForValue().get(key);
if (obj != null) { //如果不是第一次提交,返回“请勿重复提交”提示
response.setContentType("application/json;charset=utf-8");
try {
response.getWriter().write(JSON.toJSONString(InvokeResult.fail("请勿重复提交")));
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
long timeout = noRepeatForm.timeout();
if (timeout < 0) { //如果自定义超时时间设置有误,设置为默认值
timeout = 2000;
}
//表单第一次提交过,存入Key
LOGGER.info("存入key:"+key);
stringRedisTemplate.opsForValue().set(key, String.valueOf(IDTool.getId()), timeout, TimeUnit.MILLISECONDS);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!(handler instanceof HandlerMethod)) {
return;
}
Method method = ((HandlerMethod) handler).getMethod();
NoRepeatForm noRepeatForm = method.getAnnotation(NoRepeatForm.class);
if (noRepeatForm == null) {
return;
}
//请求完成,删除Key
method = ((HandlerMethod) handler).getMethod();
String key=getKey(request,method);
stringRedisTemplate.delete(key);
LOGGER.info("删除key:"+key);
}
/**
* 根据请求地址和方法获取唯一Key
* @param request 获取IP
* @param method 定位 className -> methodName
* @return key
*/
private String getKey(HttpServletRequest request, Method method){
String ip = IPUtils.getIpAddr(request);
String ipKey = String.format("%s#%s", method.getDeclaringClass().getName(), method.getName());
String key=String.format("%s_%d", ip, Math.abs(ipKey.hashCode()));
LOGGER.info("key:"+key);
return key ;
}
}
3. 在自定义WebMvcConfig配置中,添加拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private FormInterceptor formInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(formInterceptor);
}
// @Bean
// FormInterceptor formInterceptor(){
// return new FormInterceptor();
// }
}
4. 遇到的问题以及解决办法
问题:在进行拦截处理时,获取的stringRedisTemplate对象为null,导致存入数据时异常
原因:自定义拦截器没有注入成功
解决方式:1)使用@Bean注解;2)使用@Component注解