背景
- 前期项目业务已上线,需优化。多个地方避免重复提交,并且需要解析返回结果,根据返回结果的成败,补充不同的数据,再响应到前端。
- 考虑尽量减少或避免改动原代码业务方法,但是也要能避免重复提交。
- 项目具有restful风格,即同一个uri会有对应多个方法,这些方法对应 http的get/post/delete/update
- 项目方法返回的结果数据类型不统一
分析
- 减少或改动源代码业务方法,则需要统一处理,具体方案需要从如下方向考虑: 过滤器/拦截器/注解/AOP
- 方案需满足如下要求:
- 1.业务方法前判定(是否需要调用业务),是否直接返回判定结果。
- 2.业务方法后排定,是否直接返回业务数据。
- 3.业务方法后排定,需要解析业务返回的数据(数据类型不统一),并根据数据中的某些参数,判定是否需要新建返回数据,并返回数据
- 方案对比如下:
方案 | 特点 | 适用性 |
---|
过滤器 filter | 只满足要求1 | 不适用本场景 |
拦截器 HandlerInterceptorAdapter | 只满足要求1、2 | 不适用本场景 |
拦截器 ResponseBodyAdvice | 只满足要求3 | |
AOP+注解+反射 | 满足全部要求,终极大招。。 | 采用 |
要点
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface NoRepeatSubmit {
int lockTime() default CoreConst.CURRENT_LOCK_BETWEEN;
boolean freshLock() default false;
boolean useLock() default false;
}
@GetMapping("/api/xmind/list")
@NoRepeatSubmit(useLock = true)
@ResponseBody
public Response list(
@GetMapping("/api/caserepo/list")
@NoRepeatSubmit(useLock = true)
@ResponseBody
public ResultVO caseRepoList(
@Aspect
@Component
@Slf4j
public class NoRepeatSubmitAfterAop {
@Autowired
private ISysBufferLockService sysBufferLockService;
@Autowired
private ResultComponentUtil resultComponentUtil;
@Pointcut("@annotation(com.mi.info.mitest.casemanage.web.aop.NoRepeatSubmit)")
public void pointCut(){}
@After("pointCut()")
public void doAfter(JoinPoint joinPoint){
Object[] arrs = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
Class className = joinPoint.getSignature().getDeclaringType();
log.info("noRepeatSubmit doAfter--methodName:{}",methodName);
}
public void doBefore(JoinPoint joinPoint){
Object[] arrs = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
Class className = joinPoint.getSignature().getDeclaringType();
log.info("before--methodName:{}",methodName);
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String userId = UserInfoUtil.getUserId();
long nowTime = System.currentTimeMillis();
}
@Around("@annotation(noRepeatSubmit)")
public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Throwable {
Object result =null;
if(noRepeatSubmit.freshLock()){
sysBufferLockService.freshLock();
}else if(noRepeatSubmit.useLock()){
ErrorCodeEnum errorCodeEnum = sysBufferLockService.passLock();
if(errorCodeEnum.equalsByCode(ErrorCodeEnum.SUCCESS)){
result = joinPoint.proceed();
Integer code = -1;
if(result instanceof Response) {
code = ((Response) result).getCode();
}else if(result instanceof ResultVO ) {
code = ((ResultVO) result).getCode();
}
sysBufferLockService.dealLock(ErrorCodeEnum.SUCCESS.getCode().equals(code));
}else {
Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
Class returnType = method.getReturnType();
if(returnType.isAssignableFrom(Response.class)){
return ResponseBuilder.create().error(errorCodeEnum.getCode(),errorCodeEnum.getMsg(),null).build();
}else if(returnType.isAssignableFrom(ResultVO.class)){
return resultComponentUtil.error(errorCodeEnum.getCode(),errorCodeEnum.getMsg());
}
}
}
return result==null? joinPoint.proceed():result;
}
}
失败案例
@Slf4j
@ControllerAdvice
public class NoRepeatSubmitAfterInterceptor implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
System.out.println("TestResponseBodyAdvice==>beforeBodyWrite:" + o.toString() + ","
+ methodParameter);
return o;
}
}
@Slf4j
@WebInterceptor(order = Const.InterceptorOrder.AVOID_SUMBIY_AGAIN_ORDER,
includePatterns = {
"/api/**",
})
public class NoRepeatSubmitBeforeInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ISysBufferLockService sysBufferLockService;
ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
NoRepeatSubmit noRepeatSubmit=getAnno(handler,NoRepeatSubmit.class);
if (noRepeatSubmit != null ) {
boolean freshLock = noRepeatSubmit.freshLock();
boolean useLock = noRepeatSubmit.useLock();
if(freshLock){
sysBufferLockService.freshLock();
}else if(useLock){
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
NoRepeatSubmit noRepeatSubmit=getAnno(handler,NoRepeatSubmit.class);
if (noRepeatSubmit != null ) {
boolean useLock = noRepeatSubmit.useLock();
if(useLock){
boolean succ=false;
Object result = request.getAttribute("response");
sysBufferLockService.dealLock(succ);
}
}
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
NoRepeatSubmit noRepeatSubmit=getAnno(handler,NoRepeatSubmit.class);
if (noRepeatSubmit != null ) {
boolean useLock = noRepeatSubmit.useLock();
if(useLock){
boolean succ=false;
Map<String, String[]> parameterMap = request.getParameterMap();
Object result = request.getAttribute("response");
PrintWriter writer = response.getWriter();
String s = response.getWriter().toString();
response.getWriter().println();
sysBufferLockService.dealLock(succ);
}
}
super.afterCompletion(request, response, handler, ex);
}
private boolean respFail(HttpServletResponse response, ErrorCodeEnum errorCodeEnum) throws IOException {
Map<String, Object> map = new HashMap<>();
map.put("code", errorCodeEnum.getCode());
map.put("msg", errorCodeEnum.getMsg());
map.put("data", null);
String s = objectMapper.writeValueAsString(map);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.getWriter().write(s);
return false;
}
private <T extends Annotation> T getAnno(Object handler,Class<T> annotationClass){
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
return method.getAnnotation(annotationClass);
}
return null;
}
相关方案
相关资料