Spring自定义注解实现json参数传递

作者: OneFish | 来源:发表于2016-05-23 13:45 被阅读2744次

    实现(POST,GET)传参自动转换成Json对象,所有参数为Json格式然后再Base64加密

    Spring Request详解:

    Spring 的DispatcherServlet 实现了Servlet方法,来处理一次请求

    DispatcherServlet主要是执行了两个方法doService(HttpServletRequest request, HttpServletResponse response),doDispatch(HttpServletRequest request, HttpServletResponse response)

    在doDispatch方法中

        // Determine handler for the current request.
    
        mappedHandler = getHandler(processedRequest);
    

    获取当前请求的handler,也就是获取到执行当前request的bean。

    HandlerMapping有多个实现,具体的还需自己看下实现

        // Determine handler adapter for the current request.
    
        HandlerAdapterha = getHandlerAdapter(mappedHandler.getHandler());
    

    获取到当前请求的HandlerAdapter

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }
    

    获取ha.supports(handler) 返回为true的HandlerAdapter

    eg.
    如果这个bean实现了HttpRequestHandler接口则返回HttpRequestHandlerAdapter

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof HttpRequestHandler);
    }
    

    如果这个bean实现了Controller接口则返回SimpleControllerHandlerAdapter

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }
    

    目前常用的方式是通过@Controller、@RequestMapping和Spring配置中添加<context:component-scan/>

    此时使用的是RequestMappingHandlerAdapter

    RequestMappingHandlerAdapter 主要实现了请求参数的封装处理,返回参数的封装处理,支持注入argumentResolvers、returnValueHandlers、messageConverters等来实现扩展

    RequestMappingHandlerAdapter.invokeHandlerMethod.invokeAndHandle.invokeForRequest.getMethodArgumentValues

    
    if (this.argumentResolvers.supportsParameter(parameter)) {
        try {
            args[i] = this.argumentResolvers.resolveArgument(
                    parameter, mavContainer, request, this.dataBinderFactory);
            continue;
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
            }
            throw ex;
        }
    }
    
    

    首先会调用argumentResolver的supportsParameter判断是否可用,然后再执行resolveArgument方法

    SpecialArgumentsResolver,自定义argumentResolver

    通过上面我们可以发现,基于自定义注解实现Json的参数传递,需要实现自定的argumentResolver并注入到RequestMappingHandlerAdapter中

    1. 实现自定义Json注解
    
    /**
     * Created by zhaoqi on 2016/5/5.
     */
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Json {
    
        Class[] types() default java.lang.Object.class;
    
        String path() default "";
    }
    
    
    
    1. spring配置文件增加自定义argumentResolver

    注意,如果是注入的argumentResolver,会覆盖默认的argumentResolver。

    <!--RequestMappingHandlerAdapter加入自定义ArgumentsResolver-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="synchronizeOnSession" value="true" />
        <property name="argumentResolvers">
            <list>
                <bean class="com.zhaoqi.component.annotation.SpecialArgumentsResolver"/>
            </list>
        </property>
        <property name="messageConverters">
            <list>
                <ref bean="stringConverter" />
                <ref bean="jsonConverter" />
            </list>
        </property>
    </bean>
    

    如果不想覆盖默认的argumentResolver,请注入customArgumentResolvers。

    
    // Custom arguments
    
    if(getCustomArgumentResolvers() !=null) {
    
        resolvers.addAll(getCustomArgumentResolvers());
    
    }
    
    1. 实现SpecialArgumentsResolver
    
    /**
     * Created by zhaoqi on 2016/5/6.
     */
    public class SpecialArgumentsResolver implements HandlerMethodArgumentResolver {
        @Resource
        DotaJsonHttpMessageConverter dotaJsonHttpMessageConverter;
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            return parameter.hasParameterAnnotation(Json.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            return this.readArguments(webRequest, parameter, parameter.getGenericParameterType());
        }
    
        private Object readArguments(NativeWebRequest webRequest, MethodParameter parameter, Type genericParameterType) {
            HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
            Object arg =null;
            try {
                // get方式取queryString
                if (servletRequest.getMethod().equals(RequestMappingHandlerAdapter.METHOD_GET)) {
                    // base64解码
                    String decodedQueryString = new String(Base64Utils.decodeFromString(servletRequest.getQueryString()));
                    return JsonUtil.toObject(decodedQueryString, Class.forName(genericParameterType.getTypeName()));
                }
                // Json注解使用dotaJsonHttpMessageConverter读取参数
                arg = dotaJsonHttpMessageConverter.readInternal(Class.forName(genericParameterType.getTypeName()), inputMessage);
                if (null == arg) {
                    throw new HttpMessageNotReadableException("Required request body is missing: " +
                            parameter.getMethod().toGenericString());
                }
            } catch (IOException | ClassNotFoundException e) {
                //
            }
            return arg;
        }
    }
    
    

    实际使用

    @RequestMapping("/sayHi")
    @ResponseBody
    public ResponseVo getFeedback(@Json HelloRequest hello){
        ResponseVo responseVo = new ResponseVo();
        responseVo.setMsg("success");
        responseVo.setData(hello.getHello());
        return responseVo;
    }
    

    好处

    • 目前前端页面调用后台接口,GET和POST的参数形式不一样。GET方式是将参数拼接在url中,而POST是可以直接传Json对象的
      后台通过使用@Json注解,可以实现GET和POST参数的统一,如果需要切换请求方式,无需重新拼装参数
    • 本例采用的为base64加密,实际可以根据情况自行使用加密方式。可以伪装请求的参数,增强安全性

    相关文章

      网友评论

        本文标题:Spring自定义注解实现json参数传递

        本文链接:https://www.haomeiwen.com/subject/pmuarttx.html