Spring MVC中Handler、HandlerMapping和HandlerAdapter的关系

Handler、HandlerMapping和HandlerAdapter是Spring MVC中重要的组成部分。

Handler

Handler是对请求进行处理的最小单位,一个最典型的Handler即Controller,Handler用于对Request进行处理,同时将最终的处理结果写入Response。Controller中有方法叫handleRequest(),在这里就可以对Request进行处理并返回一个ModelAndView

package hello.test;


import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelMap modelMap = new ModelMap();
        modelMap.put("key", "value");
        return new ModelAndView(new MyView(), modelMap);
    }
}

HandlerMapping

HandlerMapping中保存请求的URL和handler的关系,同时其中包含了很多的方法用来添加url和handler的键值对或者是根据url的名字检索出相应的handler。

以BeanNameUrlHandlerMapping为例,在初始化阶段Spring会在其中存放扫描出的路径及其对应的处理这个URL的bean

package org.springframework.web.servlet.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.StringUtils;

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

    /**
     * Checks name and aliases of the given bean for URLs, starting with "/".
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<>();
        // 如果发现bean的名字以"/"开头,就默认是作为处理URL的bean,将其保存
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = obtainApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }

}

而在运行阶段,HandlerMapping则负责为DispatcherServlet中的处理逻辑提供根据URL查询出相应的处理这个URL的bean的功能,这个被查出的bean就是对应这个URL的handler。DispatcherServlet这个类中有一个数组变量叫handlerMappings,handlerMappings中保存所有的HandlerMapping,它的值在Spring MVC进行初始化扫描的时候进行填充,包括以下几种类型

  • SimpleUrlHandlerMapping
  • RequestMappingHandlerMapping
  • BeanNameUrlHandlerMapping
  • SimpleUrlHandlerMapping
  • WelcomePageHandlerMapping

其中在每个HandlerMapping中都有个HandlerMap对象记录所有相应类型的URL和Handler的对应关系

以下是DispatcherServlet.java中获取handler相关的代码

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

以下代码是AbstractUrlHandlerMapping.java的根据URL获取Handler的具体逻辑

@Nullable
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        // Direct match?
        Object handler = this.handlerMap.get(urlPath);
        if (handler != null) {
            // Bean name or resolved handler?
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = obtainApplicationContext().getBean(handlerName);
            }
            validateHandler(handler, request);
            return buildPathExposingHandler(handler, urlPath, urlPath, null);
        }

        // Pattern match?
        List<String> matchingPatterns = new ArrayList<>();
        for (String registeredPattern : this.handlerMap.keySet()) {
            if (getPathMatcher().match(registeredPattern, urlPath)) {
                matchingPatterns.add(registeredPattern);
            }
            else if (useTrailingSlashMatch()) {
                if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                    matchingPatterns.add(registeredPattern +"/");
                }
            }
        }

        String bestMatch = null;
        Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
        if (!matchingPatterns.isEmpty()) {
            matchingPatterns.sort(patternComparator);
            if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
                logger.trace("Matching patterns " + matchingPatterns);
            }
            bestMatch = matchingPatterns.get(0);
        }
        if (bestMatch != null) {
            handler = this.handlerMap.get(bestMatch);
            if (handler == null) {
                if (bestMatch.endsWith("/")) {
                    handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
                }
                if (handler == null) {
                    throw new IllegalStateException(
                            "Could not find handler for best pattern match [" + bestMatch + "]");
                }
            }
            // Bean name or resolved handler?
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = obtainApplicationContext().getBean(handlerName);
            }
            validateHandler(handler, request);
            String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

            // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
            // for all of them
            Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
            for (String matchingPattern : matchingPatterns) {
                if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
                    Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                    Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                    uriTemplateVariables.putAll(decodedVars);
                }
            }
            if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
                logger.trace("URI variables " + uriTemplateVariables);
            }
            return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
        }

        // No handler found...
        return null;
    }

HandlerAdapter

HandlerAdapter主要用于调用handler对请求进行处理,并返回modelAndView,如果实际使用中没有指定自定义的HandlerAdapter则Spring框架会默认提供一个SimpleControllerHandlerAdapter,以下是SimpleControllerHandlerAdapter的具体实现,可以看到它最主要的是handler()方法,在这个方法中,它利用传入的handler对另外两个参数Request和Response进行了处理,并返回了一个ModelAndView,这个ModelAndView会紧接着被框架的ViewResolver处理。

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

    @Override
    // support方法用于判断当前的HandlerAdapter是否支持这个Handler
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return ((Controller) handler).handleRequest(request, response);
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }

}

使用HandlerAdapter的逻辑同样在DispatcherServlet类中,这个类同样有一个数组handlerAdapters用于记录所有系统默认或者编程人员自定义的HandlerAdapter,在doDispatch()方法中,已为代码加上中文注释,主要逻辑是先遍历所有的HandlerMapping,从中找到要处理的Handler,接着对Handler进行遍历,从所有HandlerAdapter中找到适合待处理的HandlerAdapter,接着调用HandlerAdapter对Handler进行实际处理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                // 获取所有匹配的HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                // 实际进行处理,getHandler()返回一个可以对请求进行处理的Controller对象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            ...
        }
        ...
    }
    
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
	for (HandlerAdapter ha : this.handlerAdapters) {
		if (logger.isTraceEnabled()) {
			logger.trace("Testing handler adapter [" + ha + "]");
		}
		if (ha.supports(handler)) {
			return ha;
		}
	}
}
throw new ServletException("No adapter for handler [" + handler +
		"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

猜你喜欢

转载自blog.csdn.net/turbo_zone/article/details/84571477