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;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章