zoukankan      html  css  js  c++  java
  • SpringMVC框架中DispatcherServlet分发请求给对应处理器的流程

    写在前面

    前面已经讲解了 详细解读 DispatcherServlet 初始化过程(带时序图),了解到 DispatcherServletinitStrategies(ApplicationContext context) 方法中初始化 HandlerMapping(处理器映射),HandlerAdapter(处理器适配器),HandlerExceptionResolver(异常解析器),ViewResolver(视图解析器)等等。

    也掌握了 URL 映射是如何注册到 HandlerMapping 实现中的。

    当然,我们常用的 @RequestMapping 比较复杂,所以再往后放放。先掌握拦截器和异常处理器的使用。

    拦截器和异常处理器,都是在客户端(比如 Chrome 浏览器)请求时发挥作用的,所以在学习他们之前,我们先来了解一下 SpringMVC 框架中,DispatcherServlet 分发请求到对应处理器的流程。

    从 Servlet 规范说起

    当 Servlet 容器允许某个 servlet 对象响应请求时,就会调用 Servletvoid service(ServletRequest request, ServletResponse response) 方法。

    对于选择哪个 servlet 来响应请求的规则,百度 “servlet-mapping url-pattern”,一抓一大把。链接一篇排名靠前的 servlet的url-pattern匹配规则,介绍了四种匹配规则:

    • 精准匹配(/hello, /admin/home
    • 路径匹配(/hi/*, /hi/kendoizyu/*, /*
    • 扩展名匹配(*.action, *.jsp, *.htm
    • 缺省匹配(/

    下面这张图是 Servlet 容器调用 service 方法运行到 doDispatch 的时序图:

    • HttpServlet # service(HttpServletRequest req, HttpServletResponse resp) 主要负责区分 HTTP METHOD,然后分发给 doGet,doPost 等等方法。

    • FrameworkServet # processRequest 负责“请求上下文”的保存和清理。这部分涉及到 ThreadLocal 相关的知识,更多分析可参考 SpringMVC之RequestContextHolder分析

    • FrameworkServet # doService 为 HttpServletRequest 暴露 DispatcherServlet 框架相关的属性。

    • FrameworkServet # doDispatch 为 HttpServletRequest 获取对应的处理器,适配器,执行处理方法,并且包含拦截逻辑。

    doDispatch 中跟处理器相关的方法分别是 getHandlergetHandlerAdapterHandlerAdapter # handle

    doDispatchHandlerAdapter # handle 前后分别有拦截器相关的方法 HandlerExecutionChain # applyPreHandleapplyPostHandletriggerAfterCompletion

    本文重点关注和处理器相关的方法。

    getHandler

    为当前请求决定一个处理器。

    DispatcherServlet # getHandler

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    	if (this.handlerMappings != null) {
                    // 遍历 DispatcherServlet 中的 HandlerMapping 实例对象列表
    		for (HandlerMapping mapping : this.handlerMappings) {
                            // 具体的 HandlerMapping 实例对象调用 getHandler 方法
    			HandlerExecutionChain handler = mapping.getHandler(request);
    			if (handler != null) {
    				return handler;
    			}
    		}
    	}
    	return null;
    }
    

    问题1:成员变量 handlerMappings 是怎么来的呢?

    DispatcherServlet # initHandlerMappings -> 点击展开查看源码
    
    private void initHandlerMappings(ApplicationContext context) {
    	this.handlerMappings = null;
    	if (this.detectAllHandlerMappings) {
                    // 找出 DispatcherServlet 和 ContextLoaderListener 上下文中所有实现 HandlerMapping 接口的 Bean
    		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
    		Map matchingBeans =
    				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    		if (!matchingBeans.isEmpty()) {
    			this.handlerMappings = new ArrayList<>(matchingBeans.values());
    			// We keep HandlerMappings in sorted order.
    			AnnotationAwareOrderComparator.sort(this.handlerMappings);
    		}
    	}
    	else {
                    // 找出上下文中,名称为 handlerMapping 且实现 HandlerMapping 的 Bean
    		try {
    			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
    			this.handlerMappings = Collections.singletonList(hm);
    		}
    		catch (NoSuchBeanDefinitionException ex) {
    			// Ignore, we'll add a default HandlerMapping later.
    		}
    	}
    	// Ensure we have at least one HandlerMapping, by registering
    	// a default HandlerMapping if no other mappings are found.
    	if (this.handlerMappings == null) {
                    // Spring 框架默认的 HandlerMapping
    		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    		if (logger.isTraceEnabled()) {
    			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
    					"': using default strategies from DispatcherServlet.properties");
    		}
    	}
    }
    

    阅读源码之后,我们发现初始化 handlerMappings 的方法中,分为自定义初始化多个或者单个 HandlerMapping Bean,如果没有自定义就按照框架默认的来:

    1. 默认检测所有 HandlerMapping Bean:所有实现 HandlerMapping 接口的 Bean

    2. 设置检测唯一 HandlerMapping Bean:名称为 handlerMapping 且实现 HandlerMapping 接口的 Bean。需要对 web.xml 的 <servlet> 做一点修改:

    <init-param>
          <param-name>detectAllHandlerMappings</param-name>
          <param-value>false</param-value>
    </init-param>
    
    1. 读取框架默认文件 DipatcherServlet.properties,获取 org.springframework.web.servlet.HandlerMapping 对应的 value。(开发者无法修改)
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
    	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
    

    AbstractHandlerMapping # getHandler

    这个基类方法是通用的,学习掌握这个方法,基本上就知道 SpringMVC 获取处理器的方法。

    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    	Object handler = getHandlerInternal(request);
    	if (handler == null) {
    		handler = getDefaultHandler();
    	}
    	if (handler == null) {
    		return null;
    	}
    	// Bean name or resolved handler?
    	if (handler instanceof String) {
    		String handlerName = (String) handler;
    		handler = obtainApplicationContext().getBean(handlerName);
    	}
    	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    	if (logger.isTraceEnabled()) {
    		logger.trace("Mapped to " + handler);
    	}
    	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
    		logger.debug("Mapped to " + executionChain.getHandler());
    	}
    	if (CorsUtils.isCorsRequest(request)) {
    		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
    		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    	}
    	return executionChain;
    }
    

    getHandlerInternal 方法实现目前有 2 种,一种在子类 AbstractUrlHandlerMapping 中,另一种在子类 AbstractHandlerMethodMapping 中。

    getHandlerInternal

    URL 与 类级别的处理器 映射

    AbstractUrlHandlerMapping # getHandlerInternal -> 点击展开查看源码
    
    @Override
    @Nullable
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    	Object handler = lookupHandler(lookupPath, request);
    	if (handler == null) {
    		// We need to care for the default handler directly, since we need to
    		// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
    		Object rawHandler = null;
    		if ("/".equals(lookupPath)) {
    			rawHandler = getRootHandler();
    		}
    		if (rawHandler == null) {
    			rawHandler = getDefaultHandler();
    		}
    		if (rawHandler != null) {
    			// Bean name or resolved handler?
    			if (rawHandler instanceof String) {
    				String handlerName = (String) rawHandler;
    				rawHandler = obtainApplicationContext().getBean(handlerName);
    			}
    			validateHandler(rawHandler, request);
    			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
    		}
    	}
    	return handler;
    }
    

    执行时序图如下:

    URL 与 方法级别的处理器 映射

    AbstractHandlerMethodMapping # getHandlerInternal -> 点击展开查看源码
    
    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    	List matches = new ArrayList<>();
    	List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    	if (directPathMatches != null) {
    		addMatchingMappings(directPathMatches, matches, request);
    	}
    	if (matches.isEmpty()) {
    		// No choice but to go through all mappings...
    		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    	}
    	if (!matches.isEmpty()) {
    		Comparator comparator = new MatchComparator(getMappingComparator(request));
    		matches.sort(comparator);
    		Match bestMatch = matches.get(0);
    		if (matches.size() > 1) {
    			if (logger.isTraceEnabled()) {
    				logger.trace(matches.size() + " matching mappings: " + matches);
    			}
    			if (CorsUtils.isPreFlightRequest(request)) {
    				return PREFLIGHT_AMBIGUOUS_MATCH;
    			}
    			Match secondBestMatch = matches.get(1);
    			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
    				Method m1 = bestMatch.handlerMethod.getMethod();
    				Method m2 = secondBestMatch.handlerMethod.getMethod();
    				String uri = request.getRequestURI();
    				throw new IllegalStateException(
    						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
    			}
    		}
    		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
    		handleMatch(bestMatch.mapping, lookupPath, request);
    		return bestMatch.handlerMethod;
    	}
    	else {
    		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    	}
    }
    private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
    	for (T mapping : mappings) {
    		T match = getMatchingMapping(mapping, request);
    		if (match != null) {
    			matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
    		}
    	}
    }
    

    执行时序图如下:

    这两个处理器要详细讲解还得结合实例,这里就不多说了

    getHandlerAdapter

    通过 getHandler 方法,我们已经得到了“处理器”对象,但是“处理器”对象没有统一的接口,所以使用适配器模式进行统一适配。

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    	if (this.handlerAdapters != null) {
    		for (HandlerAdapter adapter : this.handlerAdapters) {
    			if (adapter.supports(handler)) {
    				return adapter;
    			}
    		}
    	}
    	throw new ServletException("No adapter for handler [" + handler +
    			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
    

    这里用到了 适配器模式,示意图如下:

    除了 HandlerMethod 以外,其他的几个都是方法的直接转发。右图 HandlerAdapter 虚线框内的类,这是 supports(Object handler) 的目标对象,拿其中一个举例:

    @Override
    public boolean supports(Object handler) {
    	return (handler instanceof HttpRequestHandler);
    }
    

    所谓的直接转发,看源码:

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    		throws Exception {
            // 做了一步强转,就把剩下的交给接口方法处理了!
    	((HttpRequestHandler) handler).handleRequest(request, response);
    	return null;
    }
    

    doDispatch 中执行的“处理器”方法的正是这个 HandlerAdapter # handle(HttpServletRequest request, HttpServletResponse response, Object handler)

    总结

    doDispatch 的主要流程就是 获取处理器 getHandler, 获取处理器适配器 getHandlerAdapter,执行处理器适配器的 handle 方法

    其中,getHandlerInternal 因子类的不同,而有二类不同的行为,一类是 AbstractUrlHandlerMapping,另一类是AbstractHandlerMethodMapping

  • 相关阅读:
    JavaScript: RegExp + replace
    JavaScript:布局
    DOM和BOM区别
    正则表达式
    a report about the history of pragrem
    web第6次作业position
    web api
    DOM Event:事件流动(Event Flow)
    FOR衍生对象
    前端的发展
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/DispatcherServlet-doDispatch.html
Copyright © 2011-2022 走看看