Detailed explanation of Spring's RequestMappingHandlerMapping

Deep understanding of IdentityHashMap: http://donald-draper.iteye.com/blog/2326264
Deep understanding of Collections' unmodifiableMap (Map map) method: http://donald-draper.iteye.com/blog/2326291
Spring-RequestMappingHandlerAdapter initialization and Request processing: http://donald-draper.iteye.com/blog/2326185
Let's look at what the handlerMappings initialization of DispatcherServlet does
public class DispatcherServlet extends FrameworkServlet
{
 public DispatcherServlet(WebApplicationContext webApplicationContext)
    {
        super(webApplicationContext);
        detectAllHandlerMappings = true;
        detectAllHandlerAdapters = true;
        detectAllHandlerExceptionResolvers = true;
        detectAllViewResolvers = true;
        throwExceptionIfNoHandlerFound = false;
        cleanupAfterInclude = true;
    }
   
    //Initialize the controller map
     private void initHandlerMappings(ApplicationContext context)
    {
        handlerMappings = null;
        if(detectAllHandlerMappings)
        {
            Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, org/springframework/web/servlet/HandlerMapping, true, false);
            if(!matchingBeans.isEmpty())
            {
	       //List<HashMap<String,HandlerMapping>>, Key is beanName, value is HandlerMapping instance
                handlerMappings = new ArrayList(matchingBeans.values());
                OrderComparator.sort(handlerMappings);
            }
        }
    }
    //Initialize the controller method adapter
      private void initHandlerAdapters(ApplicationContext context)
    {
        handlerAdapters = null;
        if(detectAllHandlerAdapters)
        {
            Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, org/springframework/web/servlet/HandlerAdapter, true, false);
            if(!matchingBeans.isEmpty())
            {
	        //List<HashMap<String,HandlerAdapter>>, Key is beanName, value is HandlerAdapter instance
                handlerAdapters = new ArrayList(matchingBeans.values());
                OrderComparator.sort(handlerAdapters);
            }
        }
    }

    private List handlerMappings;//List<HashMap<String,HandlerMapping>>, Key is beanName, value is HandlerMapping instance
    private List handlerAdapters;//List<HashMap<String,HandlerAdapter>>,Key为beanName,value值为HandlerAdapter实例
    private List handlerExceptionResolvers;
    private List viewResolvers;

    static
    {
        try
        {
	    //load default configuration file
            ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", org/springframework/web/servlet/DispatcherServlet);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
    }
}

//Spring log tracking we can find that the controller mapping handler of DispatcherServletd is
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet .handler.SimpleUrlHandlerMapping
Here we look at RequestMappingHandlerMapping
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
    implements EmbeddedValueResolverAware
{
//initialization
 public void afterPropertiesSet()
    {
        if(useRegisteredSuffixPatternMatch)
            fileExtensions.addAll(contentNegotiationManager.getAllFileExtensions());
        // call the initialization of the parent class
	super.afterPropertiesSet();
    }
    private boolean useSuffixPatternMatch;
    private boolean useRegisteredSuffixPatternMatch;
    private boolean useTrailingSlashMatch;
    private ContentNegotiationManager contentNegotiationManager;
    private final List fileExtensions = new ArrayList();
    private StringValueResolver embeddedValueResolver;
}
to see AbstractHandlerMethodMapping
public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMapping
    implements InitializingBean
{
  //initialization
 public void afterPropertiesSet()
    {
        //Delegate to initHandlerMethods
        initHandlerMethods ();
    }
    //Initialize the method handler
    protected void initHandlerMethods()
    {
        //Get the beanName of all bean containers
        String beanNames[] = detectHandlerMethodsInAncestorContexts ?
	BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), java/lang/Object) :
	getApplicationContext().getBeanNamesForType(java/lang/Object);
        String as [] = beanNames;
        int i = as.length;
        for(int j = 0; j < i; j++)
        {
            String beanName = as [j];
            //if the bean is a Controller
            if(!beanName.startsWith("scopedTarget.") && isHandler(getApplicationContext().getType(beanName)))
                //
		detectHandlerMethods(beanName);
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
    //Get the method handler Map
    public Map getHandlerMethods()
    {
        //return an unmodifiable Map set
        return Collections.unmodifiableMap(handlerMethods);
    }
    // leave it to subclasses to extend
      protected void handlerMethodsInitialized(Map map)
    { }
    //Delegate to RequestMappingHandlerMapping
    protected abstract boolean isHandler(Class class1);
    //Add the handler method with @RequestMapping to the handlerMethods in the method handler Map, and the urlMap in the path mapping Map
    protected void detectHandlerMethods(Object handler)
    {
        Class handlerType = (handler instanceof String) ? getApplicationContext().getType((String)handler) : handler.getClass();
        //Note that IdentityHashMap is used here
	final Map mappings = new IdentityHashMap();
        final Class userType = ClassUtils.getUserClass(handlerType);
	//Get all methods with @RequestMapping in Controller
        Set methods = HandlerMethodSelector.selectMethods(userType, new org.springframework.util.ReflectionUtils.MethodFilter() {
            public boolean matches(Method method)
            {
	        
		//Get the @RequestMapping annotation information of the method of handlerType
                Object mapping = getMappingForMethod(method, userType);
                if(mapping != null)
                {
                    mappings.put(method, mapping);
                    return true;
                } else
                {
                    return false;
                }
            }
            final Class val$userType;
            final Map val$mappings;
            final AbstractHandlerMethodMapping this$0;
            {
                this.this$0 = AbstractHandlerMethodMapping.this;
                userType = class1;
                mappings = map;
                super();
            }
        });
        Method method;
        for(Iterator iterator = methods.iterator(); iterator.hasNext();
	                 registerHandlerMethod(handler, method, mappings.get(method)))
            method = (Method)iterator.next();
    }
    protected void registerHandlerMethod(Object handler, Method method, Object mapping)
    {
        //create method handler
        HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
	//Get method handler information from handlerMethods according to @RequestMapping annotation information
        HandlerMethod oldHandlerMethod = (HandlerMethod)handlerMethods.get(mapping);
	//If it already exists, throw an unmapped exception
        if(oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod))
            throw new IllegalStateException((new StringBuilder()).append("Ambiguous mapping found. Cannot map '").append(newHandlerMethod.getBean()).append("' bean method \n").append(newHandlerMethod).append("\nto ").append(mapping).append(": There is already '").append(oldHandlerMethod.getBean()).append("' bean method\n").append(oldHandlerMethod).append(" mapped.").toString());
        //Put the method handler into handlerMethods
	handlerMethods.put(mapping, newHandlerMethod);
        if(logger.isInfoEnabled())
            logger.info((new StringBuilder()).append("Mapped \"").append(mapping).append("\" onto ").append(newHandlerMethod).toString());
        //get path set
	Set patterns = getMappingPathPatterns(mapping);
        Iterator iterator = patterns.iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            String pattern = (String)iterator.next();
            if(!getPathMatcher().isPattern(pattern))
	        //Associate the path with the mapping
                urlMap.add(pattern, mapping);
        } while(true);
    }
    //Delegate to RequestMappingInfoHandlerMapping
    protected abstract Object getMappingForMethod(Method method, Class class1);
    protected abstract Set getMappingPathPatterns(Object obj);
    //Find the appropriate HandlerMethod according to the url of the request
    protected HandlerMethod getHandlerInternal(HttpServletRequest request)
        throws Exception
    {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if(logger.isDebugEnabled())
            logger.debug((new StringBuilder()).append("Looking up handler method for path ").append(lookupPath).toString());
        //Find handlerMethod based on path and request
	HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        if(logger.isDebugEnabled())
            if(handlerMethod != null)
                logger.debug((new StringBuilder()).append("Returning handler method [").append(handlerMethod).append("]").toString());
            else
                logger.debug((new StringBuilder()).append("Did not find handler method for [").append(lookupPath).append("]").toString());
        return handlerMethod == null ? null : handlerMethod.createWithResolvedBean();
    }
      //Find handlerMethod based on path and request
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request)
        throws Exception
    {
        List matches = new ArrayList();
        List directPathMatches = (List)urlMap.get(lookupPath);
        if(directPathMatches != null)
            addMatchingMappings(directPathMatches, matches, request);
        if(matches.isEmpty())
            addMatchingMappings(handlerMethods.keySet(), matches, request);
        if(!matches.isEmpty())
        {
            Comparator comparator = new MatchComparator(getMappingComparator(request));
            Collections.sort(matches, comparator);
            if(logger.isTraceEnabled())
                logger.trace((new StringBuilder()).append("Found ").append(matches.size()).append(" matching mapping(s) for [").append(lookupPath).append("] : ").append(matches).toString());
            Match bestMatch = (Match)matches.get(0);
            if(matches.size() > 1)
            {
                Match secondBestMatch = (Match)matches.get(1);
                if(comparator.compare(bestMatch, secondBestMatch) == 0)
                {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException((new StringBuilder()).append("Ambiguous handler methods mapped for HTTP path '").append(request.getRequestURL()).append("': {").append(m1).append(", ").append(m2).append("}").toString());
                }
            }
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        } else
        {
            return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
        }
    }
    private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
    private boolean detectHandlerMethodsInAncestorContexts;
    //LinkedHashMap<RequestMappingInfo,HandlerMethod>
    private final Map handlerMethods = new LinkedHashMap();
    /LinkedHashMap<String,RequestMappingInfo>, key is path
    private final MultiValueMap urlMap = new LinkedMultiValueMap();
}

Check RequestMappingHandlerMapping.isHandler()
  //isHandler to determine whether the bean has @Controller
protected boolean isHandler(Class beanType)
    {
        return AnnotationUtils.findAnnotation(beanType, org/springframework/stereotype/Controller) != null || AnnotationUtils.findAnnotation(beanType, org/springframework/web/bind/annotation/RequestMapping) != null;
    }

查看RequestMappingHandlerMapping.getMappingForMethod()
protected volatile Object getMappingForMethod(Method method, Class class1)
    {
        return getMappingForMethod(method, class1);
    }

//Determine whether the method of handlerType has @RequestMapping and return the annotation information
protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType)
    {
        RequestMappingInfo info = null;
        RequestMapping methodAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(method,
			org/springframework/web/bind/annotation/RequestMapping);
        if(methodAnnotation != null)
        {
            RequestCondition methodCondition = getCustomMethodCondition(method);
            info = createRequestMappingInfo(methodAnnotation, methodCondition);
            RequestMapping typeAnnotation = (RequestMapping)AnnotationUtils.findAnnotation(handlerType, org/springframework/web/bind/annotation/RequestMapping);
            if(typeAnnotation != null)
            {
                RequestCondition typeCondition = getCustomTypeCondition(handlerType);
                info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
            }
        }
        return info;
    }

查看RequestMappingInfoHandlerMapping.getMappingPathPatterns()
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping
{
    protected Set getMappingPathPatterns(RequestMappingInfo info)
    {
        return info.getPatternsCondition().getPatterns();
    }
}

//RequestMappingInfo
public final class RequestMappingInfo
    implements RequestCondition
{
    public PatternsRequestCondition getPatternsCondition()
    {
        return patternsCondition;
    }
    private final PatternsRequestCondition patternsCondition;
    private final RequestMethodsRequestCondition methodsCondition;
    private final ParamsRequestCondition paramsCondition;
    private final HeadersRequestCondition headersCondition;
    private final ConsumesRequestCondition consumesCondition;
    private final ProducesRequestCondition producesCondition;
    private final RequestConditionHolder customConditionHolder;
}

View PatternsRequestCondition
public final class PatternsRequestCondition extends AbstractRequestCondition
{   
   public Set getPatterns()
    {
        return patterns;
    }
    private final Set patterns;
    private final UrlPathHelper pathHelper;
    private final PathMatcher pathMatcher;
    private final boolean useSuffixPatternMatch;
    private final boolean useTrailingSlashMatch;
    private final List fileExtensions;
}

//RequestMappingInfo
public final class RequestMappingInfo
    implements RequestCondition
{
    public PatternsRequestCondition getPatternsCondition()
    {
        return patternsCondition;
    }
    private final PatternsRequestCondition patternsCondition;
    private final RequestMethodsRequestCondition methodsCondition;
    private final ParamsRequestCondition paramsCondition;
    private final HeadersRequestCondition headersCondition;
    private final ConsumesRequestCondition consumesCondition;
    private final ProducesRequestCondition producesCondition;
    private final RequestConditionHolder customConditionHolder;
}

View PatternsRequestCondition
public final class PatternsRequestCondition extends AbstractRequestCondition
{   
   public Set getPatterns()
    {
        return patterns;
    }
    private final Set patterns;
    private final UrlPathHelper pathHelper;
    private final PathMatcher pathMatcher;
    private final boolean useSuffixPatternMatch;
    private final boolean useTrailingSlashMatch;
    private final List fileExtensions;
}

//HandlerMethod
public class HandlerMethod
{
     public HandlerMethod(Object bean, Method method)
    {
        this.bean = bean;
        beanFactory = null;
        this.method = method;
        bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        parameters = initMethodParameters();
    }

    public transient HandlerMethod(Object bean, String methodName, Class parameterTypes[])
        throws NoSuchMethodException
    {
        this.bean = bean;
        beanFactory = null;
        method = bean.getClass().getMethod(methodName, parameterTypes);
        bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        parameters = initMethodParameters();
    }

    public HandlerMethod(String beanName, BeanFactory beanFactory, Method method)
    {
        bean = beanName;
        this.beanFactory = beanFactory;
        this.method = method;
        bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        parameters = initMethodParameters();
    }

    protected HandlerMethod(HandlerMethod handlerMethod)
    {
        bean = handlerMethod.bean;
        beanFactory = handlerMethod.beanFactory;
        method = handlerMethod.method;
        bridgedMethod = handlerMethod.bridgedMethod;
        parameters = handlerMethod.parameters;
    }

    private HandlerMethod(HandlerMethod handlerMethod, Object handler)
    {       
        bean = handler;
        beanFactory = handlerMethod.beanFactory;
        method = handlerMethod.method;
        bridgedMethod = handlerMethod.bridgedMethod;
        parameters = handlerMethod.parameters;
    }
    private final Object bean;
    private final BeanFactory beanFactory;
    private final Method method;
    private final Method bridgedMethod;
    private final MethodParameter parameters[];
}

Summary:
From the above analysis, it can be seen that RequestMappingHandlerMapping, the main work is to add the Controller's RequestMapping method to the processing method mapper and path method solver.




Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327039748&siteId=291194637