spring mvc3.0权限控制

2010-10-10 20:17

spring mvc3.0权限控制

by

at 2010-10-10 12:17:13

original http://www.javaeye.com/topic/780375

新项目中用到了Spring mvc3.0,之后又升级到3.0.4,因为对静态资源处理有更好支持

<mvc:resources location="/css/" mapping="/css/" />


spring mvc 3.0最大的特别即是对restful url很友好的支持。但也带来很多不便的地方,比如权限控制,花不少时间阅读源码,发现根本无法扩展对方法级别的控制,也就是使Controller层失去了面向对象的特征,惨!不知道作者是怎么想的,Juergen Hoeller!恨死你了,居然不留一点扩展的余地,要自己写过滤和匹配映射规则虽然能行,但没必要去repeat ,最后无耐Copy源码,修改一翻,想怎么控制就怎么控制了。

总结:
     在一个项目组中,如果要使用新技术,必须要有一个人精通,碰到任何问题都能解决,否则,千万不能用新技术。

spring 配置文件如下:
<bean class="com.jquickwork.extend.AnnotationMethodHandlerAdapter">
        <property name="permissionService">
            <bean class="com.****.manage.web.common.service.PermissionServiceImpl">
                <property name="rightService" ref="RightService"></property>
                <property name="test" value="false" />
            </bean>
        </property>
    </bean>



修改源码后的 AnnotationMethodHandlerAdapter类如下:

/
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 /

package com.jquickwork.extend;

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 java.util.concurrent.ConcurrentHashMap; 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.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;

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.InitBinder; 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.RequestParam; 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.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;

import com.tuankela.manage.web.common.service.PermissionException; import com.tuankela.manage.web.common.service.PermissionService;

/* * Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} interface * that maps handler methods based on HTTP paths, HTTP methods and request parameters * expressed through the {@link RequestMapping} annotation. * * <p>Supports request parameter binding through the {@link RequestParam} annotation. * Also supports the {@link ModelAttribute} annotation for exposing model attribute * values to the view, as well as {@link InitBinder} for binder initialization methods * and {@link SessionAttributes} for automatic session management of specific attributes. * * <p>This adapter can be customized through various bean properties. * A common use case is to apply shared binder initialization logic through * a custom {@link #setWebBindingInitializer WebBindingInitializer}. * * @author Juergen Hoeller * @author Arjen Poutsma * @since 2.5 * @see #setPathMatcher * @see #setMethodNameResolver * @see #setWebBindingInitializer * @see #setSessionAttributeStore / public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, Ordered, BeanFactoryAware {

private PermissionService permissionService;
/**
 * Log category to use when no mapped handler is found for a request.
 * @see #pageNotFoundLogger
 */
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = &quot;org.springframework.web.servlet.PageNotFound&quot;;

/**
 * Additional logger to use when no mapped handler is found for a request.
 * @see #PAGE_NOT_FOUND_LOG_CATEGORY
 */
protected static final Log pageNotFoundLogger = LogFactory.getLog(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&lt;?&gt;[] messageConverters;

private int order = Ordered.LOWEST_PRECEDENCE;

private ConfigurableBeanFactory beanFactory;

private BeanExpressionContext expressionContext;

private final Map&lt;Class&lt;?&gt;, ServletHandlerMethodResolver&gt; methodResolverCache =
        new ConcurrentHashMap&lt;Class&lt;?&gt;, ServletHandlerMethodResolver&gt;();


public AnnotationMethodHandlerAdapter() {
    // no restriction of HTTP methods by default
    super(false);

    // See SPR-7316
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    stringHttpMessageConverter.setWriteAcceptCharset(false);
    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 &quot;.../*&quot; servlet mapping in web.xml).
 * &lt;p&gt;Default is &quot;false&quot;.
 * @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
 * &lt;i&gt;undecoded&lt;/i&gt; by the Servlet API, in contrast to the servlet path.
 * &lt;p&gt;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.
 * &lt;p&gt;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, &quot;UrlPathHelper must not be null&quot;);
    this.urlPathHelper = urlPathHelper;
}

/**
 * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns.
 * &lt;p&gt;Default is {@link org.springframework.util.AntPathMatcher}.
 */
public void setPathMatcher(PathMatcher pathMatcher) {
    Assert.notNull(pathMatcher, &quot;PathMatcher must not be null&quot;);
    this.pathMatcher = pathMatcher;
}

/**
 * Set the MethodNameResolver to use for resolving default handler methods
 * (carrying an empty &lt;code&gt;@RequestMapping&lt;/code&gt; annotation).
 * &lt;p&gt;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.
 * &lt;p&gt;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, &quot;SessionAttributeStore must not be null&quot;);
    this.sessionAttributeStore = sessionAttributeStore;
}

/**
 * Cache content produced by &lt;code&gt;@SessionAttributes&lt;/code&gt; annotated handlers
 * for the given number of seconds. Default is 0, preventing caching completely.
 * &lt;p&gt;In contrast to the &quot;cacheSeconds&quot; property which will apply to all general handlers
 * (but not to &lt;code&gt;@SessionAttributes&lt;/code&gt; annotated handlers), this setting will
 * apply to &lt;code&gt;@SessionAttributes&lt;/code&gt; 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.
 * &lt;p&gt;More specifically, the execution of the &lt;code&gt;handleRequestInternal&lt;/code&gt;
 * method will get synchronized if this flag is &quot;true&quot;. The best available
 * session mutex will be used for the synchronization; ideally, this will
 * be a mutex exposed by HttpSessionMutexListener.
 * &lt;p&gt;The session mutex is guaranteed to be the same object during
 * the entire lifetime of the session, available under the key defined
 * by the &lt;code&gt;SESSION_MUTEX_ATTRIBUTE&lt;/code&gt; constant. It serves as a
 * safe reference to synchronize on for locking on the current session.
 * &lt;p&gt;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).
 * &lt;p&gt;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.
 * &lt;p&gt;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.
 * &lt;p&gt;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.
 * &lt;p&gt;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.
 * &lt;p&gt;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.
 * &lt;p&gt;These converters are used to convert from and to HTTP requests and responses.
 */
public void setMessageConverters(HttpMessageConverter&lt;?&gt;[] messageConverters) {
    this.messageConverters = messageConverters;
}

/**
 * Return the message body converters that this adapter has been configured with.
 */
public HttpMessageConverter&lt;?&gt;[] getMessageConverters() {
    return messageConverters;
}

/**
 * Specify the order value for this HandlerAdapter bean.
 * &lt;p&gt;Default value is &lt;code&gt;Integer.MAX_VALUE&lt;/code&gt;, meaning that it&#39;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 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 invokeHandlerMethod(request, response, handler);
            }
        }
    }

    return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(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();

    Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    ModelAndView mav =
            methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
    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) {
    Class handlerClass = ClassUtils.getUserClass(handler);
    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.
 * &lt;p&gt;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 &lt;code&gt;null&lt;/code&gt;
 * 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.
 * &lt;p&gt;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.
 * &lt;p&gt;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&lt;Method, RequestMappingInfo&gt; mappings = new HashMap&lt;Method, RequestMappingInfo&gt;();

    private ServletHandlerMethodResolver(Class&lt;?&gt; 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) {
            RequestMappingInfo mappingInfo = new RequestMappingInfo();
            mappingInfo.patterns = mapping.value();
            if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
                mappingInfo.methods = mapping.method();
            }
            if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
                mappingInfo.params = mapping.params();
            }
            if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
                mappingInfo.headers = mapping.headers();
            }
            this.mappings.put(method, mappingInfo);
            return true;
        }
        return false;
    }

    public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
        String lookupPath = urlPathHelper.getLookupPathForRequest(request);
        Comparator&lt;String&gt; pathComparator = pathMatcher.getPatternComparator(lookupPath);
        Map&lt;RequestMappingInfo, Method&gt; targetHandlerMethods = new LinkedHashMap&lt;RequestMappingInfo, Method&gt;();
        Set&lt;String&gt; allowedMethods = new LinkedHashSet&lt;String&gt;(7);
        String resolvedMethodName = null;
        for (Method handlerMethod : getHandlerMethods()) {
            RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod);
            boolean match = false;
            if (mappingInfo.hasPatterns()) {
                List&lt;String&gt; matchingPatterns = new ArrayList&lt;String&gt;(mappingInfo.patterns.length);
                for (String pattern : mappingInfo.patterns) {
                    if (!hasTypeLevelMapping() &amp;&amp; !pattern.startsWith(&quot;/&quot;)) {
                        pattern = &quot;/&quot; + pattern;
                    }
                    String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
                    if (combinedPattern != null) {
                        if (mappingInfo.matches(request)) {
                            match = true;
                            matchingPatterns.add(combinedPattern);
                        }
                        else {
                            if (!mappingInfo.matchesRequestMethod(request)) {
                                allowedMethods.addAll(mappingInfo.methodNames());
                            }
                            break;
                        }
                    }
                }
                Collections.sort(matchingPatterns, pathComparator);
                mappingInfo.matchedPatterns = matchingPatterns;
            }
            else {
                // No paths specified: parameter match sufficient.
                match = mappingInfo.matches(request);
                if (match &amp;&amp; mappingInfo.methods.length == 0 &amp;&amp; mappingInfo.params.length == 0 &amp;&amp;
                        resolvedMethodName != null &amp;&amp; !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 &amp;&amp; oldMappedMethod != handlerMethod) {
                    if (methodNameResolver != null &amp;&amp; mappingInfo.patterns.length == 0) {
                        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(
                                &quot;Ambiguous handler methods mapped for HTTP path &#39;&quot; + lookupPath + &quot;&#39;: {&quot; +
                                        oldMappedMethod + &quot;, &quot; + handlerMethod +
                                        &quot;}. If you intend to handle the same path in multiple methods, then factor &quot; +
                                        &quot;them out into a dedicated handler class with that path mapped at the type level!&quot;);
                    }
                }
            }
        }
        if (!targetHandlerMethods.isEmpty()) {
            List&lt;RequestMappingInfo&gt; matches = new ArrayList&lt;RequestMappingInfo&gt;(targetHandlerMethods.keySet());
            RequestMappingInfoComparator requestMappingInfoComparator =
                    new RequestMappingInfoComparator(pathComparator, request);
            Collections.sort(matches, requestMappingInfoComparator);
            RequestMappingInfo bestMappingMatch = matches.get(0);
            String bestMatchedPath = bestMappingMatch.bestMatchedPattern();
            if (bestMatchedPath != null) {
                //validate permission
                permissionService.isPermit(request, bestMatchedPath ,bestMappingMatch.patterns[0]);
                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.
     * &lt;p&gt;Uses the following algorithm: &lt;ol&gt;
     * &lt;li&gt;If there is a type-level mapping with path information, it is {@linkplain
     * PathMatcher#combine(String, String) combined} with the method-level pattern.&lt;/li&gt;
     * &lt;li&gt;If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the
     * request, it is combined with the method-level pattern.&lt;/li&gt;
     * &lt;li&gt;Otherwise, the method-level pattern is returned.&lt;/li&gt;
     * &lt;/ol&gt;
     */
    private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
        if (hasTypeLevelMapping() &amp;&amp; (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
            String[] typeLevelPatterns = getTypeLevelMapping().value();
            for (String typeLevelPattern : typeLevelPatterns) {
                if (!typeLevelPattern.startsWith(&quot;/&quot;)) {
                    typeLevelPattern = &quot;/&quot; + 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) &amp;&amp; bestMatchingPattern.endsWith(&quot;*&quot;)) {
            String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
            if (!combinedPattern.equals(bestMatchingPattern) &amp;&amp;
                    (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(&#39;.&#39;) != -1;
        if (!hasSuffix &amp;&amp; pathMatcher.match(pattern + &quot;.*&quot;, lookupPath)) {
            return true;
        }
        boolean endsWithSlash = pattern.endsWith(&quot;/&quot;);
        if (!endsWithSlash &amp;&amp; pathMatcher.match(pattern + &quot;/&quot;, lookupPath)) {
            return true;
        }
        return false;
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    private void extractHandlerMethodUriTemplates(String mappedPattern,
            String lookupPath,
            HttpServletRequest request) {

        Map&lt;String, String&gt; variables =
                (Map&lt;String, String&gt;) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

        int patternVariableCount = StringUtils.countOccurrencesOf(mappedPattern, &quot;{&quot;);

        if ( (variables == null || patternVariableCount != variables.size())  
                &amp;&amp; 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);
    }

    @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 AnnotationMethodHandlerAdapter.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 AnnotationMethodHandlerAdapter.this.createHttpInputMessage(servletRequest);
    }

    @Override
    protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
        HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
        return AnnotationMethodHandlerAdapter.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);
    }

    @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({&quot;unchecked&quot;})
    protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
            throws Exception {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Map&lt;String, String&gt; uriTemplateVariables =
                (Map&lt;String, String&gt;) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
            throw new IllegalStateException(
                    &quot;Could not find @PathVariable [&quot; + pathVarName + &quot;] in @RequestMapping&quot;);
        }
        return uriTemplateVariables.get(pathVarName);
    }

    @Override
    protected Object resolveStandardArgument(Class&lt;?&gt; 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(
                        &quot;Current request is not of type [&quot; + parameterType.getName() + &quot;]: &quot; + request);
            }
            return nativeRequest;
        }
        else if (ServletResponse.class.isAssignableFrom(parameterType)) {
            this.responseArgumentUsed = true;
            Object nativeResponse = webRequest.getNativeResponse(parameterType);
            if (nativeResponse == null) {
                throw new IllegalStateException(
                        &quot;Current response is not of type [&quot; + parameterType.getName() + &quot;]: &quot; + 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(&quot;unchecked&quot;)
    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&lt;?&gt;) 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 &#39;void&#39; 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(&quot;Invalid handler method return value: &quot; + 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);
    }

    private void handleHttpEntityResponse(HttpEntity&lt;?&gt; responseEntity, ServletWebRequest webRequest)
            throws Exception {
        if (responseEntity == null) {
            return;
        }
        HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
        HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
        if (responseEntity instanceof ResponseEntity &amp;&amp; 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(&quot;unchecked&quot;)
    private void writeWithMessageConverters(Object returnValue,
            HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException {
        List&lt;MediaType&gt; acceptedMediaTypes = inputMessage.getHeaders().getAccept();
        if (acceptedMediaTypes.isEmpty()) {
            acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
        }
        MediaType.sortByQualityValue(acceptedMediaTypes);
        Class&lt;?&gt; returnValueType = returnValue.getClass();
        List&lt;MediaType&gt; allSupportedMediaTypes = new ArrayList&lt;MediaType&gt;();
        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(&quot;Written [&quot; + returnValue + &quot;] as \&quot;&quot; + contentType +
                                    &quot;\&quot; using [&quot; + messageConverter + &quot;]&quot;);
                        }
                        this.responseArgumentUsed = true;
                        return;
                    }
                }
            }
            for (HttpMessageConverter messageConverter : messageConverters) {
                allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
            }
        }
        throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
    }

}


/**
 * Holder for request mapping metadata. Allows for finding a best matching candidate.
 */
static class RequestMappingInfo {

    String[] patterns = new String[0];

    List&lt;String&gt; matchedPatterns = Collections.emptyList();

    RequestMethod[] methods = new RequestMethod[0];

    String[] params = new String[0];

    String[] headers = new String[0];

    public boolean hasPatterns() {
        return patterns.length &gt; 0;
    }

    public String bestMatchedPattern() {
        return (!this.matchedPatterns.isEmpty() ? this.matchedPatterns.get(0) : null);
    }

    public boolean matches(HttpServletRequest request) {
        return matchesRequestMethod(request) &amp;&amp; matchesParameters(request) &amp;&amp; matchesHeaders(request);
    }

    public boolean matchesHeaders(HttpServletRequest request) {
        return checkHeaders(this.headers, request);
    }

    public boolean matchesParameters(HttpServletRequest request) {
        return checkParameters(this.params, request);
    }

    public boolean matchesRequestMethod(HttpServletRequest request) {
        return checkRequestMethod(this.methods, request);
    }

    public Set&lt;String&gt; methodNames() {
        Set&lt;String&gt; methodNames = new LinkedHashSet&lt;String&gt;(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) &amp;&amp; Arrays.equals(this.methods, other.methods) &amp;&amp;
                Arrays.equals(this.params, other.params) &amp;&amp; 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 &gt; 0) {
            builder.append(&#39;,&#39;);
            builder.append(Arrays.asList(methods));
        }
        if (headers.length &gt; 0) {
            builder.append(&#39;,&#39;);
            builder.append(Arrays.asList(headers));
        }
        if (params.length &gt; 0) {
            builder.append(&#39;,&#39;);
            builder.append(Arrays.asList(params));
        }
        return builder.toString();
    }
}


/**
 * Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
 * result in:
 * &lt;ul&gt;
 * &lt;li&gt;RHIs with {@linkplain RequestMappingInfo#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.&lt;/li&gt;
 * &lt;li&gt;RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be
 * ordered before those without a method, or with more than one method.&lt;/li&gt;
 * &lt;li&gt;RHIs with more {@linkplain RequestMappingInfo#params request parameters} will be ordered before those with
 * less parameters&lt;/li&gt;
 * &lt;/ol&gt;
 */
static class RequestMappingInfoComparator implements Comparator&lt;RequestMappingInfo&gt; {

    private final Comparator&lt;String&gt; pathComparator;

    private final ServerHttpRequest request;

    RequestMappingInfoComparator(Comparator&lt;String&gt; pathComparator, HttpServletRequest request) {
        this.pathComparator = pathComparator;
        this.request = new ServletServerHttpRequest(request);
    }

    public int compare(RequestMappingInfo info1, RequestMappingInfo info2) {
        int pathComparison = pathComparator.compare(info1.bestMatchedPattern(), info2.bestMatchedPattern());
        if (pathComparison != 0) {
            return pathComparison;
        }
        int info1ParamCount = info1.params.length;
        int info2ParamCount = info2.params.length;
        if (info1ParamCount != info2ParamCount) {
            return info2ParamCount - info1ParamCount;
        }
        int info1HeaderCount = info1.headers.length;
        int info2HeaderCount = info2.headers.length;
        if (info1HeaderCount != info2HeaderCount) {
            return info2HeaderCount - info1HeaderCount;
        }
        int acceptComparison = compareAcceptHeaders(info1, info2);
        if (acceptComparison != 0) {
            return acceptComparison;
        }
        int info1MethodCount = info1.methods.length;
        int info2MethodCount = info2.methods.length;
        if (info1MethodCount == 0 &amp;&amp; info2MethodCount &gt; 0) {
            return 1;
        }
        else if (info2MethodCount == 0 &amp;&amp; info1MethodCount &gt; 0) {
            return -1;
        }
        else if (info1MethodCount == 1 &amp; info2MethodCount &gt; 1) {
            return -1;
        }
        else if (info2MethodCount == 1 &amp; info1MethodCount &gt; 1) {
            return 1;
        }
        return 0;
    }

    private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) {
        List&lt;MediaType&gt; requestAccepts = request.getHeaders().getAccept();
        MediaType.sortByQualityValue(requestAccepts);

        List&lt;MediaType&gt; info1Accepts = getAcceptHeaderValue(info1);
        List&lt;MediaType&gt; 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&lt;MediaType&gt; infoAccepts, MediaType requestAccept) {
        for (int i = 0; i &lt; infoAccepts.size(); i++) {
            MediaType info1Accept = infoAccepts.get(i);
            if (requestAccept.includes(info1Accept)) {
                return i;
            }
        }
        return -1;
    }

    private List&lt;MediaType&gt; getAcceptHeaderValue(RequestMappingInfo info) {
        for (String header : info.headers) {
            int separator = header.indexOf(&#39;=&#39;);
            if (separator != -1) {
                String key = header.substring(0, separator);
                String value = header.substring(separator + 1);
                if (&quot;Accept&quot;.equalsIgnoreCase(key)) {
                    return MediaType.parseMediaTypes(value);
                }
            }
        }
        return Collections.emptyList();
    }
}





///----------------------org.springframework.web.servlet.mvc.annotation.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(&#39;=&#39;);
            if (separator == -1) {
                if (param.startsWith(&quot;!&quot;)) {
                    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(&#39;=&#39;);
            if (separator == -1) {
                if (header.startsWith(&quot;!&quot;)) {
                    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&lt;MediaType&gt; requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key));
                    List&lt;MediaType&gt; valueMediaTypes = MediaType.parseMediaTypes(value);
                    boolean found = false;
                    for (Iterator&lt;MediaType&gt; valIter = valueMediaTypes.iterator(); valIter.hasNext() &amp;&amp; !found;) {
                        MediaType valueMediaType = valIter.next();
                        for (Iterator&lt;MediaType&gt; reqIter = requestMediaTypes.iterator(); reqIter.hasNext() &amp;&amp; !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 &quot;Accept&quot;.equalsIgnoreCase(headerName) || &quot;Content-Type&quot;.equalsIgnoreCase(headerName);
}


public void setPermissionService(PermissionService permissionService) {
    this.permissionService = permissionService;
}

}

      <br><br>
      作者: <a href="http://jiangshaolin.javaeye.com">jiangshaolin</a> 
      <br>
      声明: 本文系JavaEye网站发布的原创文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!
      <br><br>
      <span style="color:red">
        <a href="http://www.javaeye.com/topic/780375" style="color:red">已有 <strong>0</strong> 人发表回复,猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
      </span>
      <br><br><br>

JavaEye推荐