• SpringMVC(八):使用Servlet原生API作为Spring MVC hanlder方法的参数


    在SpringMVC开发中,是有场景需要在Handler方法中直接使用ServletAPI。

    在Spring MVC Handler的方法中都支持哪些Servlet API作为参数呢?

    --Response 

    * <li>{@link ServletResponse}
    * <li>{@link OutputStream}
    * <li>{@link Writer}

    --Request

    * <li>{@link WebRequest}
    * <li>{@link ServletRequest}
    * <li>{@link MultipartRequest}
    * <li>{@link HttpSession}
    * <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
    * <li>{@link Principal}
    * <li>{@link InputStream}
    * <li>{@link Reader}
    * <li>{@link HttpMethod} (as of Spring 4.0)
    * <li>{@link Locale}
    * <li>{@link TimeZone} (as of Spring 4.0)
    * <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)

    备注: 不同的SpringMVC版本,可能这里的标准不一致,我这里的SpringMVC版本是5.02。

    为什么Spring MVC handler方法中支持上边的ServletAPI呢?

    下边先看一个Spring MVC handler方法中使用servlet API作为参数的例子,之后调试来解析为什么。

    新建一个handler类:TestServletAPI.java

     1 package com.dx.springlearn.hanlders;
     2 
     3 import javax.servlet.http.HttpServletRequest;
     4 import javax.servlet.http.HttpServletResponse;
     5 
     6 import org.springframework.stereotype.Controller;
     7 import org.springframework.web.bind.annotation.RequestMapping;
     8 
     9 @Controller
    10 public class TestServletAPI {
    11     private final String SUCCESS = "success";
    12 
    13     @RequestMapping("/testServletAPI")
    14     public String testServletAPI(HttpServletRequest request, HttpServletResponse response) {
    15         System.out.println("testServletAPI,request:" + request + ",response:" + response);
    16         return SUCCESS;
    17     }
    18 }

    备注:在第15行打上断掉,后边debug调试时需要这么做。

    在index.jsp上添加链接:

        <a href="testServletAPI">test ServletAPI</a>

    测试打印结果为:

    testServletAPI,request:org.apache.catalina.connector.RequestFacade@534683c2,response:org.apache.catalina.connector.ResponseFacade@77dbf19f

    断点分析:

    下边截图中是handler参数类型解析映射的位置:

    下边截图的函数中针对不同的参数解析映射方式不同:

    下图可以说明两个问题:

    1)除了上图中providedArgs方式处理handler方法参数映射解析外,其他解析器都是实现了HandlerMethodArgumentResolver接口类;

    2)其中跟ServletAPI接口类有关的handler方法参数映射解析器包含(该结论是调试时确定,但从图中不能完全确定):

    org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver,
    org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver

    通过查看org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver,
    org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver源代码,就可以明白为什么Spring MVC handler方法只支持那些Servlet API:

    org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver.java

    package org.springframework.web.servlet.mvc.method.annotation;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.Writer;
    import javax.servlet.ServletResponse;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.lang.Nullable;
    import org.springframework.web.bind.support.WebDataBinderFactory;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.ModelAndViewContainer;
    
    /**
     * Resolves response-related method argument values of types:
     * <ul>
     * <li>{@link ServletResponse}
     * <li>{@link OutputStream}
     * <li>{@link Writer}
     * </ul>
     *
     * @author Arjen Poutsma
     * @author Rossen Stoyanchev
     * @author Juergen Hoeller
     * @since 3.1
     */
    public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            Class<?> paramType = parameter.getParameterType();
            return (ServletResponse.class.isAssignableFrom(paramType) ||
                    OutputStream.class.isAssignableFrom(paramType) ||
                    Writer.class.isAssignableFrom(paramType));
        }
    
        /**
         * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
         * {@code false} to indicate that the method signature provides access
         * to the response. If subsequently the underlying method returns
         * {@code null}, the request is considered directly handled.
         */
        @Override
        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
            if (mavContainer != null) {
                mavContainer.setRequestHandled(true);
            }
    
            Class<?> paramType = parameter.getParameterType();
    
            // ServletResponse, HttpServletResponse
            if (ServletResponse.class.isAssignableFrom(paramType)) {
                return resolveNativeResponse(webRequest, paramType);
            }
    
            // ServletResponse required for all further argument types
            return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
        }
    
        private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
            T nativeResponse = webRequest.getNativeResponse(requiredType);
            if (nativeResponse == null) {
                throw new IllegalStateException(
                        "Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
            }
            return nativeResponse;
        }
    
        private Object resolveArgument(Class<?> paramType, ServletResponse response) throws IOException {
            if (OutputStream.class.isAssignableFrom(paramType)) {
                return response.getOutputStream();
            }
            else if (Writer.class.isAssignableFrom(paramType)) {
                return response.getWriter();
            }
    
            // Should never happen...
            throw new UnsupportedOperationException("Unknown parameter type: " + paramType);
        }
    
    }

    org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver.java

    package org.springframework.web.servlet.mvc.method.annotation;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.Reader;
    import java.security.Principal;
    import java.time.ZoneId;
    import java.util.Locale;
    import java.util.TimeZone;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import javax.servlet.http.PushBuilder;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.http.HttpMethod;
    import org.springframework.lang.Nullable;
    import org.springframework.util.ClassUtils;
    import org.springframework.web.bind.support.WebDataBinderFactory;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.context.request.WebRequest;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.ModelAndViewContainer;
    import org.springframework.web.multipart.MultipartRequest;
    import org.springframework.web.servlet.support.RequestContextUtils;
    
    /**
     * Resolves request-related method argument values of the following types:
     * <ul>
     * <li>{@link WebRequest}
     * <li>{@link ServletRequest}
     * <li>{@link MultipartRequest}
     * <li>{@link HttpSession}
     * <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
     * <li>{@link Principal}
     * <li>{@link InputStream}
     * <li>{@link Reader}
     * <li>{@link HttpMethod} (as of Spring 4.0)
     * <li>{@link Locale}
     * <li>{@link TimeZone} (as of Spring 4.0)
     * <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)
     * </ul>
     *
     * @author Arjen Poutsma
     * @author Rossen Stoyanchev
     * @author Juergen Hoeller
     * @since 3.1
     */
    public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
        @Nullable
        private static Class<?> pushBuilder;
    
        static {
            try {
                pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder",
                        ServletRequestMethodArgumentResolver.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                // Servlet 4.0 PushBuilder not found - not supported for injection
                pushBuilder = null;
            }
        }
    
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            Class<?> paramType = parameter.getParameterType();
            return (WebRequest.class.isAssignableFrom(paramType) ||
                    ServletRequest.class.isAssignableFrom(paramType) ||
                    MultipartRequest.class.isAssignableFrom(paramType) ||
                    HttpSession.class.isAssignableFrom(paramType) ||
                    (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
                    Principal.class.isAssignableFrom(paramType) ||
                    InputStream.class.isAssignableFrom(paramType) ||
                    Reader.class.isAssignableFrom(paramType) ||
                    HttpMethod.class == paramType ||
                    Locale.class == paramType ||
                    TimeZone.class == paramType ||
                    ZoneId.class == paramType);
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
            Class<?> paramType = parameter.getParameterType();
    
            // WebRequest / NativeWebRequest / ServletWebRequest
            if (WebRequest.class.isAssignableFrom(paramType)) {
                if (!paramType.isInstance(webRequest)) {
                    throw new IllegalStateException(
                            "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
                }
                return webRequest;
            }
    
            // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
            if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
                return resolveNativeRequest(webRequest, paramType);
            }
    
            // HttpServletRequest required for all further argument types
            return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
        }
    
        private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
            T nativeRequest = webRequest.getNativeRequest(requiredType);
            if (nativeRequest == null) {
                throw new IllegalStateException(
                        "Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
            }
            return nativeRequest;
        }
    
        @Nullable
        private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
            if (HttpSession.class.isAssignableFrom(paramType)) {
                HttpSession session = request.getSession();
                if (session != null && !paramType.isInstance(session)) {
                    throw new IllegalStateException(
                            "Current session is not of type [" + paramType.getName() + "]: " + session);
                }
                return session;
            }
            else if (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {
                return PushBuilderDelegate.resolvePushBuilder(request, paramType);
            }
            else if (InputStream.class.isAssignableFrom(paramType)) {
                InputStream inputStream = request.getInputStream();
                if (inputStream != null && !paramType.isInstance(inputStream)) {
                    throw new IllegalStateException(
                            "Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
                }
                return inputStream;
            }
            else if (Reader.class.isAssignableFrom(paramType)) {
                Reader reader = request.getReader();
                if (reader != null && !paramType.isInstance(reader)) {
                    throw new IllegalStateException(
                            "Request body reader is not of type [" + paramType.getName() + "]: " + reader);
                }
                return reader;
            }
            else if (Principal.class.isAssignableFrom(paramType)) {
                Principal userPrincipal = request.getUserPrincipal();
                if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
                    throw new IllegalStateException(
                            "Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
                }
                return userPrincipal;
            }
            else if (HttpMethod.class == paramType) {
                return HttpMethod.resolve(request.getMethod());
            }
            else if (Locale.class == paramType) {
                return RequestContextUtils.getLocale(request);
            }
            else if (TimeZone.class == paramType) {
                TimeZone timeZone = RequestContextUtils.getTimeZone(request);
                return (timeZone != null ? timeZone : TimeZone.getDefault());
            }
            else if (ZoneId.class == paramType) {
                TimeZone timeZone = RequestContextUtils.getTimeZone(request);
                return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
            }
    
            // Should never happen...
            throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
        }
    
    
        /**
         * Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.
         */
        private static class PushBuilderDelegate {
    
            @Nullable
            public static Object resolvePushBuilder(HttpServletRequest request, Class<?> paramType) {
                PushBuilder pushBuilder = request.newPushBuilder();
                if (pushBuilder != null && !paramType.isInstance(pushBuilder)) {
                    throw new IllegalStateException(
                            "Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);
                }
                return pushBuilder;
    
            }
        }
    
    }
  • 相关阅读:
    HDU 2047 阿牛的EOF牛肉串
    HDU 2015 偶数求和
    HDU 2029 算菜价
    HDU 2028 Lowest Common Multiple Plus
    动态函数库设计
    静态函数库设计
    Linux编程规范
    Linux应用程序地址布局
    Core Dump 程序故障分析
    W-D-S-UART编程
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/8196391.html
Copyright © 2020-2023  润新知