SpringMVC自定义绑定参数、及Controller增强处理

在基于SpringMVC的WEB服务开发过程中,经常会遇到如入参解密、返参加密这样的过程,这个过程可能需要很多代码可能只需要一行调用,但是每个Controller都需要调用这样一个方法就可能会感觉繁琐,而且不利于主要业务代码阅读,有时会影响项目架构。本文通过一个小案例,基于Spring AOP思想来解决类似这样的问题,希望能起到举一反三的作用,在开发过程中能够解决一些问题、减小代码的重复,用最少的代码完成功能。

SpringMVC的基本过程

使用SpringMVC时,所有的请求都是最先经过DispatcherServlet的,然后由DispatcherServlet选择合适的HandlerMapping和HandlerAdapter来处理请求,HandlerMapping的作用就是找到请求所对应的方法,而HandlerAdapter则来处理和请求相关的的各种事情,比如参数如何绑定到@RequestMapping的方法中,参数如何返回到Model中等。
SpringMVC Controller参数解析是由RequestMappingHandlerAdapter类进行处理的,这个类通过一个List维护不仅维护了SpringMVC自带的参数解析方法,同时也支持用记自定义的参数解析器。SpringMVC通过查询List中的解析器,如果找到一个能支持解析的对象,就会让它去解析而不会再去查找其它的解析器。所以一般来讲Controller方法的入参模型实体类只有一个对应的解析器去处理。
参数绑定接口是HandlerMethodArgumentResolver,接口定义了两个方法: boolean supportsParameter(MethodParameter parameter)需要返回该解析器是否支持参数 parameter的封装,支持就返回true;然后具体的解析业务由接口的另一个方法 Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
处理,并返回处理后的对象。
以下代码支持有@MobileRequestParam注解的Controller方法参数进行参数绑定。

public class EhrMobileRequestParamResolver implements HandlerMethodArgumentResolver {

    private static final Logger logger = LoggerFactory.getLogger(EhrMobileRequestParamResolver.class);

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(MobileRequestParam.class);
    }

    /**
     * 解析json串,并绑定传参
     */
    @Override
    public Object resolveArgument(MethodParameter parameter,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception {
        Object resBean = null;
        try {
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            Object data = request.getAttribute("data");
            Object version = request.getAttribute("version");
            if (StringUtils.isNotBlank(data.toString())) {
                JSONObject jsonObject = JSONObject.fromObject(data);
                jsonObject.put("version", version);
                resBean = JSONObject.toBean(jsonObject, parameter.getParameterType());
            }
        } catch (Exception e) {
            logger.error(">>>> 参数解析出错  {} <<<<", e);
        }
        return resBean;
    }

}

使用上述自定义绑定参数,需要application-servlet.xml配置如下

```xml
<mvc:annotation-driven >
    <mvc:argument-resolvers>
        <bean class="com.project.EhrMobileRequestParamResolver" />
    </mvc:argument-resolvers>
</mvc:annotation-driven>

Controller方法返回参数增强处理

@ControllerAdvice是SpringMVC的Controller增加处理类,其原理是使用AOP对Controller控制器进行增强,这样就可以对控制器的方法调用前后进行处理。使用了这个注解,表示Controller会在方法调用前后根据注解类定义的方法进行相应的处理。开启注解需要在application-servlet.xml配置中添加扫描注解。
ResponseBodyAdvice接口对调用带有@ResponseBody、@ResponseEntity注解的Controller方法执行后,而在返回到View层前进行自定义的处理。与HandlerMethodArgumentResolver 接口一样有两个方法:supports方法的作用是判断是否需要对此Controller方法进行处理,Controller方法及返参的信息由对象MethodParameter returnType维护,如果支持处理则返回ture;然后再由beforeBodyWrite方法进行自定义处理,顾名思义,这个方法就是在使用相应的HttpMessageConvert 进行write之前会被调用,就是一个切面方法。
如下的代码是在Model writer之前对其中一个参数进行加密。

@ControllerAdvice
public class EhrMobileResponseAdvice implements ResponseBodyAdvice<Object>{

    @Override
    public boolean supports(MethodParameter returnType,
            Class<? extends HttpMessageConverter<?>> converterType) {
        return DtoPublicResponse.class.isAssignableFrom(returnType.getParameterType());
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request, ServerHttpResponse response) {
        HttpServletRequest req = ((ServletServerHttpRequest) request).getServletRequest();
        System.err.println(returnType.getParameterType());
        if (body instanceof DtoPublicMobileResponseObj) {
            DtoPublicMobileResponseObj obj = (DtoPublicMobileResponseObj) body;
            Object reStr = obj.getResultMessage();
            if (null != reStr && StringUtils.isNotBlank(reStr.toString()) && !"1".equals(req.getAttribute("token"))) {
                obj.setResultMessage(SecurityUtil.getEncryptDataForParam(reStr, req.getAttribute("version").toString()));
                return obj;
            }
        }
        // TODO Auto-generated method stub
        return body;
    }

}

猜你喜欢

转载自blog.csdn.net/ljyhust/article/details/79114892