• 扩展spring mvc的拦截器,实现AOP的环绕增加效果


    原因:

    1. spring mvc拦截器通过中postHander方法中只有ModelAndView类型的结果,如果@Controller返回的是@ResponseBody的字符串类型,ModelAndView的值就为null,就不能在postHandler中把结果写入日志或做其它对结果的处理。

    public void postHandle(
    			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    			throws Exception {
    	}

    2.AOP的环绕增强能够拿到@Controller的结果,但前置检查只能检查方法中定义的参数,没有固定HttpServletRequest参数,如之前我一篇文章中的例子http://blog.csdn.net/wuxinzaiyu/article/details/8608394,

    request需要这样获取,并且具体执行方法的参数第一个必须是HttpServletRequest才行,即使获取的时候可以循环下判断类型得到,也不是很方便

    获取参数方法:

    HttpServletRequest request = (HttpServletRequest) pjp.getArgs()[0];  

    执行方法代码:

    1. @ResponseBody  
    2.     @RequestMapping(value="/openapi/v1/account/kp")  
    3.     public String kp(HttpServletRequest request) {  
    4.         System.out.println("kp:123");  
    5.         return "kp:123";  
    6.     }  

    目的:结合两种方法,即能实现环绕效果又不限制具体方法的写法。


    用同事写好的代码为例,开工:

    1.以spring mvc的AnnotationMethodHandlerAdapter为基础,在invokeHandlerProcess方法中增加前后执行内容,达到环绕增强

    package com.test.interceptor;
    
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.Reader;
    import java.io.Writer;
    import java.lang.reflect.Method;
    import java.security.Principal;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.LinkedHashMap;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Set;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.config.BeanExpressionContext;
    import org.springframework.beans.factory.config.BeanExpressionResolver;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
    import org.springframework.core.Ordered;
    import org.springframework.core.ParameterNameDiscoverer;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.converter.ByteArrayHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.http.converter.xml.SourceHttpMessageConverter;
    import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.http.server.ServletServerHttpResponse;
    import org.springframework.ui.ExtendedModelMap;
    import org.springframework.ui.Model;
    import org.springframework.util.AntPathMatcher;
    import org.springframework.util.Assert;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.ObjectUtils;
    import org.springframework.util.PathMatcher;
    import org.springframework.util.StringUtils;
    import org.springframework.validation.support.BindingAwareModelMap;
    import org.springframework.web.HttpMediaTypeNotAcceptableException;
    import org.springframework.web.HttpRequestMethodNotSupportedException;
    import org.springframework.web.HttpSessionRequiredException;
    import org.springframework.web.bind.MissingServletRequestParameterException;
    import org.springframework.web.bind.ServletRequestDataBinder;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.bind.annotation.SessionAttributes;
    import org.springframework.web.bind.annotation.support.HandlerMethodInvoker;
    import org.springframework.web.bind.annotation.support.HandlerMethodResolver;
    import org.springframework.web.bind.support.DefaultSessionAttributeStore;
    import org.springframework.web.bind.support.SessionAttributeStore;
    import org.springframework.web.bind.support.WebArgumentResolver;
    import org.springframework.web.bind.support.WebBindingInitializer;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.context.request.RequestScope;
    import org.springframework.web.context.request.ServletWebRequest;
    import org.springframework.web.multipart.MultipartRequest;
    import org.springframework.web.servlet.HandlerAdapter;
    import org.springframework.web.servlet.HandlerMapping;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
    import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
    import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
    import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver;
    import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
    import org.springframework.web.servlet.support.RequestContextUtils;
    import org.springframework.web.servlet.support.WebContentGenerator;
    import org.springframework.web.util.UrlPathHelper;
    import org.springframework.web.util.WebUtils;
    
    /**
     * 从spring的AnnotationMethodHandlerAdapter拷贝而来,增加了执行method前后的扩展点,顺便纪录下执行时间
     * @author zhipeng.qzp
     */
    public class ExtendableHandlerAdapter extends WebContentGenerator implements HandlerAdapter, Ordered, BeanFactoryAware {
    
        /**
         * Log category to use when no mapped handler is found for a request.
         * @see #pageNotFoundLogger
         */
        public static final String                                PAGE_NOT_FOUND_LOG_CATEGORY             = "org.springframework.web.servlet.PageNotFound";
    
        /**
         * Additional logger to use when no mapped handler is found for a request.
         * @see #PAGE_NOT_FOUND_LOG_CATEGORY
         */
        protected static final Logger                             logger                                  = LoggerFactory.getLogger(PAGE_NOT_FOUND_LOG_CATEGORY);
    
        private UrlPathHelper                                     urlPathHelper                           = new UrlPathHelper();
    
        private PathMatcher                                       pathMatcher                             = new AntPathMatcher();
    
        private MethodNameResolver                                methodNameResolver                      = new InternalPathMethodNameResolver();
    
        private WebBindingInitializer                             webBindingInitializer;
    
        private SessionAttributeStore                             sessionAttributeStore                   = new DefaultSessionAttributeStore();
    
        private int                                               cacheSecondsForSessionAttributeHandlers = 0;
    
        private boolean                                           synchronizeOnSession                    = false;
    
        private ParameterNameDiscoverer                           parameterNameDiscoverer                 = new LocalVariableTableParameterNameDiscoverer();
    
        private WebArgumentResolver[]                             customArgumentResolvers;
    
        private ModelAndViewResolver[]                            customModelAndViewResolvers;
    
        private HttpMessageConverter<?>[]                         messageConverters;
    
        private int                                               order                                   = Ordered.HIGHEST_PRECEDENCE;
    
        private ConfigurableBeanFactory                           beanFactory;
    
        private BeanExpressionContext                             expressionContext;
    
        private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache                     = new HashMap<Class<?>, ServletHandlerMethodResolver>();
    
        private List<IHandlerInterceptor>                         invokeInterceptors                      = new ArrayList<IHandlerInterceptor>();
    
        @SuppressWarnings("rawtypes")
        public ExtendableHandlerAdapter() {
            // no restriction of HTTP methods by default
            super(false);
    
            // See SPR-7316
            StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
            stringHttpMessageConverter.setWriteAcceptCharset(false);
            this.messageConverters = new HttpMessageConverter[] { new ByteArrayHttpMessageConverter(), stringHttpMessageConverter, new SourceHttpMessageConverter(), new XmlAwareFormHttpMessageConverter() };
        }
    
        /**
         * Set if URL lookup should always use the full path within the current
         * servlet context. Else, the path within the current servlet mapping is
         * used if applicable (that is, in the case of a ".../*" servlet mapping in
         * web.xml).
         * <p>
         * Default is "false".
         * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
         */
        public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
            this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
        }
    
        /**
         * Set if context path and request URI should be URL-decoded. Both are
         * returned <i>undecoded</i> by the Servlet API, in contrast to the servlet
         * path.
         * <p>
         * Uses either the request encoding or the default encoding according to the
         * Servlet spec (ISO-8859-1).
         * @see org.springframework.web.util.UrlPathHelper#setUrlDecode
         */
        public void setUrlDecode(boolean urlDecode) {
            this.urlPathHelper.setUrlDecode(urlDecode);
        }
    
        /**
         * Set the UrlPathHelper to use for resolution of lookup paths.
         * <p>
         * Use this to override the default UrlPathHelper with a custom subclass, or
         * to share common UrlPathHelper settings across multiple HandlerMappings
         * and HandlerAdapters.
         */
        public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
            Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
            this.urlPathHelper = urlPathHelper;
        }
    
        /**
         * Set the PathMatcher implementation to use for matching URL paths against
         * registered URL patterns.
         * <p>
         * Default is {@link org.springframework.util.AntPathMatcher}.
         */
        public void setPathMatcher(PathMatcher pathMatcher) {
            Assert.notNull(pathMatcher, "PathMatcher must not be null");
            this.pathMatcher = pathMatcher;
        }
    
        /**
         * Set the MethodNameResolver to use for resolving default handler methods
         * (carrying an empty <code>@RequestMapping</code> annotation).
         * <p>
         * Will only kick in when the handler method cannot be resolved uniquely
         * through the annotation metadata already.
         */
        public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
            this.methodNameResolver = methodNameResolver;
        }
    
        /**
         * Specify a WebBindingInitializer which will apply pre-configured
         * configuration to every DataBinder that this controller uses.
         */
        public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
            this.webBindingInitializer = webBindingInitializer;
        }
    
        /**
         * Specify the strategy to store session attributes with.
         * <p>
         * Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore} , storing session attributes in the HttpSession, using the same attribute
         * name as in the model.
         */
        public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
            Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null");
            this.sessionAttributeStore = sessionAttributeStore;
        }
    
        /**
         * Cache content produced by <code>@SessionAttributes</code> annotated
         * handlers for the given number of seconds. Default is 0, preventing
         * caching completely.
         * <p>
         * In contrast to the "cacheSeconds" property which will apply to all
         * general handlers (but not to <code>@SessionAttributes</code> annotated
         * handlers), this setting will apply to <code>@SessionAttributes</code>
         * annotated handlers only.
         * @see #setCacheSeconds
         * @see org.springframework.web.bind.annotation.SessionAttributes
         */
        public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {
            this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;
        }
    
        /**
         * Set if controller execution should be synchronized on the session, to
         * serialize parallel invocations from the same client.
         * <p>
         * More specifically, the execution of the
         * <code>handleRequestInternal</code> method will get synchronized if this
         * flag is "true". The best available session mutex will be used for the
         * synchronization; ideally, this will be a mutex exposed by
         * HttpSessionMutexListener.
         * <p>
         * The session mutex is guaranteed to be the same object during the entire
         * lifetime of the session, available under the key defined by the
         * <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a safe
         * reference to synchronize on for locking on the current session.
         * <p>
         * In many cases, the HttpSession reference itself is a safe mutex as well,
         * since it will always be the same object reference for the same active
         * logical session. However, this is not guaranteed across different servlet
         * containers; the only 100% safe way is a session mutex.
         * @see org.springframework.web.util.HttpSessionMutexListener
         * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
         */
        public void setSynchronizeOnSession(boolean synchronizeOnSession) {
            this.synchronizeOnSession = synchronizeOnSession;
        }
    
        /**
         * Set the ParameterNameDiscoverer to use for resolving method parameter
         * names if needed (e.g. for default attribute names).
         * <p>
         * Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
         */
        public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
            this.parameterNameDiscoverer = parameterNameDiscoverer;
        }
    
        /**
         * Set a custom WebArgumentResolvers to use for special method parameter
         * types.
         * <p>
         * Such a custom WebArgumentResolver will kick in first, having a chance to
         * resolve an argument value before the standard argument handling kicks in.
         */
        public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
            this.customArgumentResolvers = new WebArgumentResolver[] { argumentResolver };
        }
    
        /**
         * Set one or more custom WebArgumentResolvers to use for special method
         * parameter types.
         * <p>
         * Any such custom WebArgumentResolver will kick in first, having a chance
         * to resolve an argument value before the standard argument handling kicks
         * in.
         */
        public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
            this.customArgumentResolvers = argumentResolvers;
        }
    
        /**
         * Set a custom ModelAndViewResolvers to use for special method return
         * types.
         * <p>
         * Such a custom ModelAndViewResolver will kick in first, having a chance to
         * resolve a return value before the standard ModelAndView handling kicks
         * in.
         */
        public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
            this.customModelAndViewResolvers = new ModelAndViewResolver[] { customModelAndViewResolver };
        }
    
        /**
         * Set one or more custom ModelAndViewResolvers to use for special method
         * return types.
         * <p>
         * Any such custom ModelAndViewResolver will kick in first, having a chance
         * to resolve a return value before the standard ModelAndView handling kicks
         * in.
         */
        public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
            this.customModelAndViewResolvers = customModelAndViewResolvers;
        }
    
        /**
         * Set the message body converters to use.
         * <p>
         * These converters are used to convert from and to HTTP requests and
         * responses.
         */
        public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
            this.messageConverters = messageConverters;
        }
    
        /**
         * Return the message body converters that this adapter has been configured
         * with.
         */
        public HttpMessageConverter<?>[] getMessageConverters() {
            return messageConverters;
        }
    
        /**
         * Specify the order value for this HandlerAdapter bean.
         * <p>
         * Default value is <code>Integer.MAX_VALUE</code>, meaning that it's
         * non-ordered.
         * @see org.springframework.core.Ordered#getOrder()
         */
        public void setOrder(int order) {
            this.order = order;
        }
    
        public int getOrder() {
            return this.order;
        }
    
        public void setBeanFactory(BeanFactory beanFactory) {
            if (beanFactory instanceof ConfigurableBeanFactory) {
                this.beanFactory = (ConfigurableBeanFactory) beanFactory;
                this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope());
            }
        }
    
        public void setInvokeInterceptors(List<?> invokeInterceptors) {
            for (Object obj : invokeInterceptors) {
                if (obj instanceof IHandlerInterceptor) {
                    this.invokeInterceptors.add((IHandlerInterceptor) obj);
                }
            }
        }
    
        public boolean supports(Object handler) {
            return getMethodResolver(handler).hasHandlerMethods();
        }
    
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
                // Always prevent caching in case of session attribute management.
                checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
                // Prepare cached set of session attributes names.
            } else {
                // Uses configured default cacheSeconds setting.
                checkAndPrepare(request, response, true);
            }
    
            // Execute invokeHandlerMethod in synchronized block if required.
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized (mutex) {
                        return invokeHandlerProcess(request, response, handler);
                    }
                }
            }
    
            return invokeHandlerProcess(request, response, handler);
        }
    
        protected ModelAndView invokeHandlerProcess(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
            Method handlerMethod = methodResolver.resolveHandlerMethod(request);
            ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            ExtendedModelMap implicitModel = new BindingAwareModelMap();
    
            if (null != this.invokeInterceptors && !this.invokeInterceptors.isEmpty()) {
                for (IHandlerInterceptor interceptor : this.invokeInterceptors) {
                    interceptor.preInvoke(handlerMethod, handler, webRequest);
                }
            }
    
            Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    
            ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
    
            if (null != this.invokeInterceptors && !this.invokeInterceptors.isEmpty()) {
                for (int i = this.invokeInterceptors.size() - 1; i >= 0; i--) {
                    this.invokeInterceptors.get(i).postInvoke(handlerMethod, handler, webRequest, ((null != result) ? result : mav));
                }
            }
    
            methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
    
            return mav;
        }
    
        public long getLastModified(HttpServletRequest request, Object handler) {
            return -1;
        }
    
        /**
         * Build a HandlerMethodResolver for the given handler type.
         */
        private ServletHandlerMethodResolver getMethodResolver(Object handler) {
            // if(true) throw new RuntimeException();
            Class<?> handlerClass = ClassUtils.getUserClass(handler);
            synchronized (this.methodResolverCache) {
                ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
                if (resolver == null) {
                    resolver = new ServletHandlerMethodResolver(handlerClass);
                    this.methodResolverCache.put(handlerClass, resolver);
                }
                return resolver;
            }
        }
    
        /**
         * Template method for creating a new ServletRequestDataBinder instance.
         * <p>
         * The default implementation creates a standard ServletRequestDataBinder.
         * This can be overridden for custom ServletRequestDataBinder subclasses.
         * @param request
         *            current HTTP request
         * @param target
         *            the target object to bind onto (or <code>null</code> if the
         *            binder is just used to convert a plain parameter value)
         * @param objectName
         *            the objectName of the target object
         * @return the ServletRequestDataBinder instance to use
         * @throws Exception
         *             in case of invalid state or arguments
         * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest)
         * @see ServletRequestDataBinder#convertIfNecessary(Object, Class<?>,
         *      org.springframework.core.MethodParameter)
         */
        protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName) throws Exception {
            return new ServletRequestDataBinder(target, objectName);
        }
    
        /**
         * Template method for creating a new HttpInputMessage instance.
         * <p>
         * The default implementation creates a standard {@link ServletServerHttpRequest}. This can be overridden for custom {@code HttpInputMessage} implementations
         * @param servletRequest
         *            current HTTP request
         * @return the HttpInputMessage instance to use
         * @throws Exception
         *             in case of errors
         */
        protected HttpInputMessage createHttpInputMessage(HttpServletRequest servletRequest) throws Exception {
            return new ServletServerHttpRequest(servletRequest);
        }
    
        /**
         * Template method for creating a new HttpOuputMessage instance.
         * <p>
         * The default implementation creates a standard {@link ServletServerHttpResponse}. This can be overridden for custom {@code HttpOutputMessage} implementations
         * @param servletResponse
         *            current HTTP response
         * @return the HttpInputMessage instance to use
         * @throws Exception
         *             in case of errors
         */
        protected HttpOutputMessage createHttpOutputMessage(HttpServletResponse servletResponse) throws Exception {
            return new ServletServerHttpResponse(servletResponse);
        }
    
        /**
         * Servlet-specific subclass of {@link HandlerMethodResolver}.
         */
        private class ServletHandlerMethodResolver extends HandlerMethodResolver {
    
            private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>();
    
            private ServletHandlerMethodResolver(Class<?> handlerType) {
                init(handlerType);
            }
    
            @Override
            protected boolean isHandlerMethod(Method method) {
                if (this.mappings.containsKey(method)) {
                    return true;
                }
                RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
                if (mapping != null) {
                    String[] patterns = mapping.value();
                    RequestMethod[] methods = new RequestMethod[0];
                    String[] params = new String[0];
                    String[] headers = new String[0];
                    if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
                        methods = mapping.method();
                    }
                    if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
                        params = mapping.params();
                    }
                    if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
                        headers = mapping.headers();
                    }
                    RequestMappingInfo mappingInfo = new RequestMappingInfo(patterns, methods, params, headers);
                    this.mappings.put(method, mappingInfo);
                    return true;
                }
                return false;
            }
    
            public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
                String lookupPath = urlPathHelper.getLookupPathForRequest(request);
                Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
                Map<RequestSpecificMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestSpecificMappingInfo, Method>();
                Set<String> allowedMethods = new LinkedHashSet<String>(7);
                String resolvedMethodName = null;
                for (Method handlerMethod : getHandlerMethods()) {
                    RequestSpecificMappingInfo mappingInfo = new RequestSpecificMappingInfo(this.mappings.get(handlerMethod));
                    boolean match = false;
                    if (mappingInfo.hasPatterns()) {
                        for (String pattern : mappingInfo.getPatterns()) {
                            if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
                                pattern = "/" + pattern;
                            }
                            String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
                            if (combinedPattern != null) {
                                if (mappingInfo.matches(request)) {
                                    match = true;
                                    mappingInfo.addMatchedPattern(combinedPattern);
                                } else {
                                    if (!mappingInfo.matchesRequestMethod(request)) {
                                        allowedMethods.addAll(mappingInfo.methodNames());
                                    }
                                    break;
                                }
                            }
                        }
                        mappingInfo.sortMatchedPatterns(pathComparator);
                    } else {
                        // No paths specified: parameter match sufficient.
                        match = mappingInfo.matches(request);
                        if (match && mappingInfo.getMethodCount() == 0 && mappingInfo.getParamCount() == 0 && resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
                            match = false;
                        } else {
                            if (!mappingInfo.matchesRequestMethod(request)) {
                                allowedMethods.addAll(mappingInfo.methodNames());
                            }
                        }
                    }
                    if (match) {
                        Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
                        if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
                            if (methodNameResolver != null && !mappingInfo.hasPatterns()) {
                                if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
                                    if (resolvedMethodName == null) {
                                        resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
                                    }
                                    if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
                                        oldMappedMethod = null;
                                    }
                                    if (!resolvedMethodName.equals(handlerMethod.getName())) {
                                        if (oldMappedMethod != null) {
                                            targetHandlerMethods.put(mappingInfo, oldMappedMethod);
                                            oldMappedMethod = null;
                                        } else {
                                            targetHandlerMethods.remove(mappingInfo);
                                        }
                                    }
                                }
                            }
                            if (oldMappedMethod != null) {
                                throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" + oldMappedMethod + ", " + handlerMethod + "}. If you intend to handle the same path in multiple methods, then factor "
                                        + "them out into a dedicated handler class with that path mapped at the type level!");
                            }
                        }
                    }
                }
                if (!targetHandlerMethods.isEmpty()) {
                    List<RequestSpecificMappingInfo> matches = new ArrayList<RequestSpecificMappingInfo>(targetHandlerMethods.keySet());
                    RequestSpecificMappingInfoComparator requestMappingInfoComparator = new RequestSpecificMappingInfoComparator(pathComparator, request);
                    Collections.sort(matches, requestMappingInfoComparator);
                    RequestSpecificMappingInfo bestMappingMatch = matches.get(0);
                    String bestMatchedPath = bestMappingMatch.bestMatchedPattern();
                    if (bestMatchedPath != null) {
                        extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
                    }
                    return targetHandlerMethods.get(bestMappingMatch);
                } else {
                    if (!allowedMethods.isEmpty()) {
                        throw new HttpRequestMethodNotSupportedException(request.getMethod(), StringUtils.toStringArray(allowedMethods));
                    }
                    throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap());
                }
            }
    
            /**
             * Determines the combined pattern for the given methodLevelPattern and
             * path.
             * <p>
             * Uses the following algorithm:
             * <ol>
             * <li>If there is a type-level mapping with path information, it is {@linkplain PathMatcher#combine(String, String) combined} with the
             * method-level pattern.</li>
             * <li>If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best
             * matching pattern} in the request, it is combined with the
             * method-level pattern.</li>
             * <li>Otherwise, the method-level pattern is returned.</li>
             * </ol>
             */
            private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
                if (hasTypeLevelMapping() && (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
                    String[] typeLevelPatterns = getTypeLevelMapping().value();
                    for (String typeLevelPattern : typeLevelPatterns) {
                        if (!typeLevelPattern.startsWith("/")) {
                            typeLevelPattern = "/" + typeLevelPattern;
                        }
                        String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);
                        if (isPathMatchInternal(combinedPattern, lookupPath)) {
                            return combinedPattern;
                        }
                    }
                    return null;
                }
                String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
                if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) {
                    String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
                    if (!combinedPattern.equals(bestMatchingPattern) && (isPathMatchInternal(combinedPattern, lookupPath))) {
                        return combinedPattern;
                    }
                }
                if (isPathMatchInternal(methodLevelPattern, lookupPath)) {
                    return methodLevelPattern;
                }
                return null;
            }
    
            private boolean isPathMatchInternal(String pattern, String lookupPath) {
                if (pattern.equals(lookupPath) || pathMatcher.match(pattern, lookupPath)) {
                    return true;
                }
                boolean hasSuffix = pattern.indexOf('.') != -1;
                if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
                    return true;
                }
                boolean endsWithSlash = pattern.endsWith("/");
                if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
                    return true;
                }
                return false;
            }
    
            @SuppressWarnings("unchecked")
            private void extractHandlerMethodUriTemplates(String mappedPattern, String lookupPath, HttpServletRequest request) {
    
                Map<String, String> variables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
    
                int patternVariableCount = StringUtils.countOccurrencesOf(mappedPattern, "{");
    
                if ((variables == null || patternVariableCount != variables.size()) && pathMatcher.match(mappedPattern, lookupPath)) {
                    variables = pathMatcher.extractUriTemplateVariables(mappedPattern, lookupPath);
                    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
                }
            }
        }
    
        /**
         * Servlet-specific subclass of {@link HandlerMethodInvoker}.
         */
        private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
    
            private boolean responseArgumentUsed = false;
    
            private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
                super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer, customArgumentResolvers, messageConverters);
            }
    
            @SuppressWarnings("rawtypes")
            @Override
            protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
                throw new MissingServletRequestParameterException(paramName, paramType.getSimpleName());
            }
    
            @Override
            protected void raiseSessionRequiredException(String message) throws Exception {
                throw new HttpSessionRequiredException(message);
            }
    
            @Override
            protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
    
                return ExtendableHandlerAdapter.this.createBinder(webRequest.getNativeRequest(HttpServletRequest.class), target, objectName);
            }
    
            @Override
            protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
                ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
                servletBinder.bind(webRequest.getNativeRequest(ServletRequest.class));
            }
    
            @Override
            protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
                return ExtendableHandlerAdapter.this.createHttpInputMessage(servletRequest);
            }
    
            @Override
            protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
                HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
                return ExtendableHandlerAdapter.this.createHttpOutputMessage(servletResponse);
            }
    
            @Override
            protected Object resolveDefaultValue(String value) {
                if (beanFactory == null) {
                    return value;
                }
                String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
                BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
                if (exprResolver == null) {
                    return value;
                }
                return exprResolver.evaluate(placeholdersResolved, expressionContext);
            }
    
            @SuppressWarnings("rawtypes")
            @Override
            protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest) throws Exception {
    
                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
                Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
                if (Cookie.class.isAssignableFrom(paramType)) {
                    return cookieValue;
                } else if (cookieValue != null) {
                    return urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
                } else {
                    return null;
                }
            }
    
            @Override
            @SuppressWarnings({ "unchecked" })
            protected String resolvePathVariable(String pathVarName, @SuppressWarnings("rawtypes") Class paramType, NativeWebRequest webRequest) throws Exception {
    
                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
                Map<String, String> uriTemplateVariables = (Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
                if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
                    throw new IllegalStateException("Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
                }
                return uriTemplateVariables.get(pathVarName);
            }
    
            @Override
            protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
                HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
                HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
    
                if (ServletRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(parameterType)) {
                    Object nativeRequest = webRequest.getNativeRequest(parameterType);
                    if (nativeRequest == null) {
                        throw new IllegalStateException("Current request is not of type [" + parameterType.getName() + "]: " + request);
                    }
                    return nativeRequest;
                } else if (ServletResponse.class.isAssignableFrom(parameterType)) {
                    this.responseArgumentUsed = true;
                    Object nativeResponse = webRequest.getNativeResponse(parameterType);
                    if (nativeResponse == null) {
                        throw new IllegalStateException("Current response is not of type [" + parameterType.getName() + "]: " + response);
                    }
                    return nativeResponse;
                } else if (HttpSession.class.isAssignableFrom(parameterType)) {
                    return request.getSession();
                } else if (Principal.class.isAssignableFrom(parameterType)) {
                    return request.getUserPrincipal();
                } else if (Locale.class.equals(parameterType)) {
                    return RequestContextUtils.getLocale(request);
                } else if (InputStream.class.isAssignableFrom(parameterType)) {
                    return request.getInputStream();
                } else if (Reader.class.isAssignableFrom(parameterType)) {
                    return request.getReader();
                } else if (OutputStream.class.isAssignableFrom(parameterType)) {
                    this.responseArgumentUsed = true;
                    return response.getOutputStream();
                } else if (Writer.class.isAssignableFrom(parameterType)) {
                    this.responseArgumentUsed = true;
                    return response.getWriter();
                }
                return super.resolveStandardArgument(parameterType, webRequest);
            }
    
            @SuppressWarnings({ "unchecked", "rawtypes" })
            public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue, ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
    
                ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
                if (responseStatusAnn != null) {
                    HttpStatus responseStatus = responseStatusAnn.value();
                    String reason = responseStatusAnn.reason();
                    if (!StringUtils.hasText(reason)) {
                        webRequest.getResponse().setStatus(responseStatus.value());
                    } else {
                        webRequest.getResponse().sendError(responseStatus.value(), reason);
                    }
    
                    // to be picked up by the RedirectView
                    webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
    
                    responseArgumentUsed = true;
                }
    
                // Invoke custom resolvers if present...
                if (customModelAndViewResolvers != null) {
                    for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
                        ModelAndView mav = mavResolver.resolveModelAndView(handlerMethod, handlerType, returnValue, implicitModel, webRequest);
                        if (mav != ModelAndViewResolver.UNRESOLVED) {
                            return mav;
                        }
                    }
                }
    
                if (returnValue instanceof HttpEntity) {
                    handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
                    return null;
                } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
                    handleResponseBody(returnValue, webRequest);
                    return null;
                } else if (returnValue instanceof ModelAndView) {
                    ModelAndView mav = (ModelAndView) returnValue;
                    mav.getModelMap().mergeAttributes(implicitModel);
                    return mav;
                } else if (returnValue instanceof Model) {
                    return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
                } else if (returnValue instanceof View) {
                    return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
                } else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
                    addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
                    return new ModelAndView().addAllObjects(implicitModel);
                } else if (returnValue instanceof Map) {
                    return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
                } else if (returnValue instanceof String) {
                    return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
                } else if (returnValue == null) {
                    // Either returned null or was 'void' return.
                    if (this.responseArgumentUsed || webRequest.isNotModified()) {
                        return null;
                    } else {
                        // Assuming view name translation...
                        return new ModelAndView().addAllObjects(implicitModel);
                    }
                } else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
                    // Assume a single model attribute...
                    addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
                    return new ModelAndView().addAllObjects(implicitModel);
                } else {
                    throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
                }
            }
    
            private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws Exception {
                if (returnValue == null) {
                    return;
                }
                HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
                HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
                writeWithMessageConverters(returnValue, inputMessage, outputMessage);
            }
    
            @SuppressWarnings("rawtypes")
            private void handleHttpEntityResponse(HttpEntity<?> responseEntity, ServletWebRequest webRequest) throws Exception {
                if (responseEntity == null) {
                    return;
                }
                HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
                HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
                if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) {
                    ((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity) responseEntity).getStatusCode());
                }
                HttpHeaders entityHeaders = responseEntity.getHeaders();
                if (!entityHeaders.isEmpty()) {
                    outputMessage.getHeaders().putAll(entityHeaders);
                }
                Object body = responseEntity.getBody();
                if (body != null) {
                    writeWithMessageConverters(body, inputMessage, outputMessage);
                } else {
                    // flush headers
                    outputMessage.getBody();
                }
            }
    
            @SuppressWarnings({ "unchecked", "rawtypes" })
            private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage, HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException {
                List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
                if (acceptedMediaTypes.isEmpty()) {
                    acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
                }
                MediaType.sortByQualityValue(acceptedMediaTypes);
                Class<?> returnValueType = returnValue.getClass();
                List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
                if (getMessageConverters() != null) {
                    for (MediaType acceptedMediaType : acceptedMediaTypes) {
                        for (HttpMessageConverter messageConverter : getMessageConverters()) {
                            if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
                                messageConverter.write(returnValue, acceptedMediaType, outputMessage);
                                if (logger.isDebugEnabled()) {
                                    MediaType contentType = outputMessage.getHeaders().getContentType();
                                    if (contentType == null) {
                                        contentType = acceptedMediaType;
                                    }
                                    logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + messageConverter + "]");
                                }
                                this.responseArgumentUsed = true;
                                return;
                            }
                        }
                    }
                    for (HttpMessageConverter messageConverter : messageConverters) {
                        allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
                    }
                }
                throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
            }
    
        }
    
        /**
         * Holder for request mapping metadata.
         */
        static class RequestMappingInfo {
    
            private final String[]        patterns;
    
            private final RequestMethod[] methods;
    
            private final String[]        params;
    
            private final String[]        headers;
    
            RequestMappingInfo(String[] patterns, RequestMethod[] methods, String[] params, String[] headers) {
                this.patterns = patterns != null ? patterns : new String[0];
                this.methods = methods != null ? methods : new RequestMethod[0];
                this.params = params != null ? params : new String[0];
                this.headers = headers != null ? headers : new String[0];
            }
    
            public boolean hasPatterns() {
                return patterns.length > 0;
            }
    
            public String[] getPatterns() {
                return patterns;
            }
    
            public int getMethodCount() {
                return methods.length;
            }
    
            public int getParamCount() {
                return params.length;
            }
    
            public int getHeaderCount() {
                return headers.length;
            }
    
            public boolean matches(HttpServletRequest request) {
                return matchesRequestMethod(request) && matchesParameters(request) && matchesHeaders(request);
            }
    
            public boolean matchesHeaders(HttpServletRequest request) {
                return ServletAnnotationMappingUtils.checkHeaders(this.headers, request);
            }
    
            public boolean matchesParameters(HttpServletRequest request) {
                return ServletAnnotationMappingUtils.checkParameters(this.params, request);
            }
    
            public boolean matchesRequestMethod(HttpServletRequest request) {
                return ServletAnnotationMappingUtils.checkRequestMethod(this.methods, request);
            }
    
            public Set<String> methodNames() {
                Set<String> methodNames = new LinkedHashSet<String>(methods.length);
                for (RequestMethod method : methods) {
                    methodNames.add(method.name());
                }
                return methodNames;
            }
    
            @Override
            public boolean equals(Object obj) {
                RequestMappingInfo other = (RequestMappingInfo) obj;
                return (Arrays.equals(this.patterns, other.patterns) && Arrays.equals(this.methods, other.methods) && Arrays.equals(this.params, other.params) && Arrays.equals(this.headers, other.headers));
            }
    
            @Override
            public int hashCode() {
                return (Arrays.hashCode(this.patterns) * 23 + Arrays.hashCode(this.methods) * 29 + Arrays.hashCode(this.params) * 31 + Arrays.hashCode(this.headers));
            }
    
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(Arrays.asList(patterns));
                if (methods.length > 0) {
                    builder.append(',');
                    builder.append(Arrays.asList(methods));
                }
                if (headers.length > 0) {
                    builder.append(',');
                    builder.append(Arrays.asList(headers));
                }
                if (params.length > 0) {
                    builder.append(',');
                    builder.append(Arrays.asList(params));
                }
                return builder.toString();
            }
        }
    
        /**
         * Subclass of {@link RequestMappingInfo} that holds request-specific data.
         */
        static class RequestSpecificMappingInfo extends RequestMappingInfo {
    
            private final List<String> matchedPatterns = new ArrayList<String>();
    
            RequestSpecificMappingInfo(String[] patterns, RequestMethod[] methods, String[] params, String[] headers) {
                super(patterns, methods, params, headers);
            }
    
            RequestSpecificMappingInfo(RequestMappingInfo other) {
                super(other.patterns, other.methods, other.params, other.headers);
            }
    
            public void addMatchedPattern(String matchedPattern) {
                matchedPatterns.add(matchedPattern);
            }
    
            public void sortMatchedPatterns(Comparator<String> pathComparator) {
                Collections.sort(matchedPatterns, pathComparator);
            }
    
            public String bestMatchedPattern() {
                return (!this.matchedPatterns.isEmpty() ? this.matchedPatterns.get(0) : null);
            }
        }
    
        /**
         * Comparator capable of sorting {@link RequestSpecificMappingInfo}s (RHIs)
         * so that sorting a list with this comparator will result in:
         * <ul>
         * <li>RHIs with {@linkplain AnnotationMethodHandlerAdapter.RequestSpecificMappingInfo#matchedPatterns
         * better matched paths} take prescedence over those with a weaker match (as
         * expressed by the {@linkplain PathMatcher#getPatternComparator(String)
         * path pattern comparator}.) Typically, this means that patterns without
         * wild cards and uri templates will be ordered before those without.</li>
         * <li>RHIs with one single {@linkplain RequestMappingInfo#methods request
         * method} will be ordered before those without a method, or with more than
         * one method.</li>
         * <li>RHIs with more {@linkplain RequestMappingInfo#params request
         * parameters} will be ordered before those with less parameters</li> </ol>
         */
        static class RequestSpecificMappingInfoComparator implements Comparator<RequestSpecificMappingInfo> {
    
            private final Comparator<String> pathComparator;
    
            private final ServerHttpRequest  request;
    
            RequestSpecificMappingInfoComparator(Comparator<String> pathComparator, HttpServletRequest request) {
                this.pathComparator = pathComparator;
                this.request = new ServletServerHttpRequest(request);
            }
    
            public int compare(RequestSpecificMappingInfo info1, RequestSpecificMappingInfo info2) {
                int pathComparison = pathComparator.compare(info1.bestMatchedPattern(), info2.bestMatchedPattern());
                if (pathComparison != 0) {
                    return pathComparison;
                }
                int info1ParamCount = info1.getParamCount();
                int info2ParamCount = info2.getParamCount();
                if (info1ParamCount != info2ParamCount) {
                    return info2ParamCount - info1ParamCount;
                }
                int info1HeaderCount = info1.getHeaderCount();
                int info2HeaderCount = info2.getHeaderCount();
                if (info1HeaderCount != info2HeaderCount) {
                    return info2HeaderCount - info1HeaderCount;
                }
                int acceptComparison = compareAcceptHeaders(info1, info2);
                if (acceptComparison != 0) {
                    return acceptComparison;
                }
                int info1MethodCount = info1.getMethodCount();
                int info2MethodCount = info2.getMethodCount();
                if (info1MethodCount == 0 && info2MethodCount > 0) {
                    return 1;
                } else if (info2MethodCount == 0 && info1MethodCount > 0) {
                    return -1;
                } else if (info1MethodCount == 1 & info2MethodCount > 1) {
                    return -1;
                } else if (info2MethodCount == 1 & info1MethodCount > 1) {
                    return 1;
                }
                return 0;
            }
    
            private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) {
                List<MediaType> requestAccepts = request.getHeaders().getAccept();
                MediaType.sortByQualityValue(requestAccepts);
    
                List<MediaType> info1Accepts = getAcceptHeaderValue(info1);
                List<MediaType> info2Accepts = getAcceptHeaderValue(info2);
    
                for (MediaType requestAccept : requestAccepts) {
                    int pos1 = indexOfIncluded(info1Accepts, requestAccept);
                    int pos2 = indexOfIncluded(info2Accepts, requestAccept);
                    if (pos1 != pos2) {
                        return pos2 - pos1;
                    }
                }
                return 0;
            }
    
            private int indexOfIncluded(List<MediaType> infoAccepts, MediaType requestAccept) {
                for (int i = 0; i < infoAccepts.size(); i++) {
                    MediaType info1Accept = infoAccepts.get(i);
                    if (requestAccept.includes(info1Accept)) {
                        return i;
                    }
                }
                return -1;
            }
    
            private List<MediaType> getAcceptHeaderValue(RequestMappingInfo info) {
                for (String header : info.headers) {
                    int separator = header.indexOf('=');
                    if (separator != -1) {
                        String key = header.substring(0, separator);
                        String value = header.substring(separator + 1);
                        if ("Accept".equalsIgnoreCase(key)) {
                            return MediaType.parseMediaTypes(value);
                        }
                    }
                }
                return Collections.emptyList();
            }
        }
    
        static class ServletAnnotationMappingUtils {
    
            /**
             * Check whether the given request matches the specified request
             * methods.
             * @param methods
             *            the HTTP request methods to check against
             * @param request
             *            the current HTTP request to check
             */
            public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
                if (ObjectUtils.isEmpty(methods)) {
                    return true;
                }
                for (RequestMethod method : methods) {
                    if (method.name().equals(request.getMethod())) {
                        return true;
                    }
                }
                return false;
            }
    
            /**
             * Check whether the given request matches the specified parameter
             * conditions.
             * @param params
             *            the parameter conditions, following {@link RequestMapping#params()}
             * @param request
             *            the current HTTP request to check
             */
            public static boolean checkParameters(String[] params, HttpServletRequest request) {
                if (!ObjectUtils.isEmpty(params)) {
                    for (String param : params) {
                        int separator = param.indexOf('=');
                        if (separator == -1) {
                            if (param.startsWith("!")) {
                                if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
                                    return false;
                                }
                            } else if (!WebUtils.hasSubmitParameter(request, param)) {
                                return false;
                            }
                        } else {
                            String key = param.substring(0, separator);
                            String value = param.substring(separator + 1);
                            if (!value.equals(request.getParameter(key))) {
                                return false;
                            }
                        }
                    }
                }
                return true;
            }
    
            /**
             * Check whether the given request matches the specified header
             * conditions.
             * @param headers
             *            the header conditions, following {@link RequestMapping#headers()}
             * @param request
             *            the current HTTP request to check
             */
            public static boolean checkHeaders(String[] headers, HttpServletRequest request) {
                if (!ObjectUtils.isEmpty(headers)) {
                    for (String header : headers) {
                        int separator = header.indexOf('=');
                        if (separator == -1) {
                            if (header.startsWith("!")) {
                                if (request.getHeader(header.substring(1)) != null) {
                                    return false;
                                }
                            } else if (request.getHeader(header) == null) {
                                return false;
                            }
                        } else {
                            String key = header.substring(0, separator);
                            String value = header.substring(separator + 1);
                            if (isMediaTypeHeader(key)) {
                                List<MediaType> requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key));
                                List<MediaType> valueMediaTypes = MediaType.parseMediaTypes(value);
                                boolean found = false;
                                for (Iterator<MediaType> valIter = valueMediaTypes.iterator(); valIter.hasNext() && !found;) {
                                    MediaType valueMediaType = valIter.next();
                                    for (Iterator<MediaType> reqIter = requestMediaTypes.iterator(); reqIter.hasNext() && !found;) {
                                        MediaType requestMediaType = reqIter.next();
                                        if (valueMediaType.includes(requestMediaType)) {
                                            found = true;
                                        }
                                    }
    
                                }
                                if (!found) {
                                    return false;
                                }
                            } else if (!value.equals(request.getHeader(key))) {
                                return false;
                            }
                        }
                    }
                }
                return true;
            }
    
            private static boolean isMediaTypeHeader(String headerName) {
                return "Accept".equalsIgnoreCase(headerName) || "Content-Type".equalsIgnoreCase(headerName);
            }
    
        }
    
    }
    


    2.定义拦截器接口,上面的代码428行已写了类型名称IHandlerInterceptor

    package com.test.interceptor;
    
    
    import java.lang.reflect.Method;
    
    import org.springframework.web.context.request.ServletWebRequest;
    
    /**
     * @author zhipeng.qzp
     */
    public interface IHandlerInterceptor {
    
        public void preInvoke(Method handlerMethod, Object handler, ServletWebRequest webRequest) throws Exception;
    
        public void postInvoke(Method handlerMethod, Object handler, ServletWebRequest webRequest, Object handlerReturn) throws Exception;
    
    }


    3.写一个具体的拦截器,实现接口的方法

    /**
     * API访问权限验证拦截器
     * @author zhipeng.qzp
     */
    public class ApiAuthInterceptor implements IHandlerInterceptor {
    
        @Override
        public void preInvoke(Method handlerMethod, Object handler, ServletWebRequest webRequest) throws ReturnBoException {
            /* 接口request参数检查 */
            HttpServletRequest request = (HttpServletRequest) webRequest.getRequest();
            try {
                //TODO 权限检查
            }
            finally {
                //TODO log
            }
            system.out.println("preInvoke");
        }
    
        @Override
        public void postInvoke(Method handlerMethod, Object handler, ServletWebRequest webRequest, Object handlerReturn) {
            //TODO log
            system.out.println("postInvoke");
        }
    
    }

    4.Spring xml配置

    <bean class="com.test.interceptor.ExtendableHandlerAdapter">
    		<property name="invokeInterceptors">
    			<list>
    				<bean class="com.test.interceptor.ApiAuthInterceptor" />
    			</list>
    		</property>
    	</bean>


    5.完工了,运行下就可以看到打出了两个print字符串,在postInvoke中可以通过handlerReturn拿到方法的结果。

  • 相关阅读:
    文本设置超过几行显示...
    CSS内置的几个标准的伪类和伪对象
    正则替换URL地址为链接地址
    移动端某些机型touchend事件触发鸡肋
    zepto 扩展prevAll(),nextAll()方法
    火狐下window.event获取undefined问题
    哪些情况下margin-left和padding-left无效?
    兵部-2048
    HTML5 将成为 HTML、XHTML 以及 HTML DOM 的新标准。HTML 的上一个版本诞生于 1999 年。自从那以后,Web 世界已经经历了巨变。HTML5 仍处于完善之中。然而,大部分现代浏览器已经具备了某些 HTML5 支持。
    二分查找
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/2989630.html
Copyright © 2020-2023  润新知