SpringMvc源码分析(三) 请求执行过程之获取MethodHandler

news2024/10/6 10:38:57

1.请求是如何关联到DispatcherServlet的  

 DispatcherServlet是Servlet的实现,遵循Servlet生命周期的规则。

Servlet的生命周期即其出生到死亡的过程中分别会调用Servlet里的以下方法:

加载和实例化:可以参考SpringMvc源码分析一

init方法:初始化,在整个 servlet 生命周期中,init() 方法只会被调用一次,即服务器启动后第一次请求时会调用一次,后续请求不再调用。

service方法:每次请求都会调用该service方法。

destory方法:重启加载或者重启容器时会调用该方法。

      当前端向Tomcat服务器发起请求时,被Tomcat容器里管理的DispatcherServlet接收到Tomcat监听到的请求信息,第一次调用init方法初始化,然后再调用Service方法。

以下是DispatcherServlet的继承实现树

      先分析init方法,根据DispatcherServlet类的继承实现树,其实际调用的时HttpServletBean里的init方法

	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
        //重点关注此处
		// Let subclasses do whatever initialization they like.
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

       在该方法中initServletBean方法是一个未实现的方法。其实际实现的方法在FrameworkServlet类的initServletBean中

	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
            //重点关注此处
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

 分析FrameworkServlet类里的initWebApplicationContext方法。

	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
            //重点关注此处
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

上面源码中的onRefresh最后调用的是DispatcherServlet类里的initStrategies方法。

	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
	protected void initStrategies(ApplicationContext context) {
        //初始化文件处理解析器
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
        //对DispatcherServlet类中的handlerMappings(处理器映射)属性赋值
		initHandlerMappings(context);
        //对DispatcherServlet类中的handlerAdapters(适配器映射)属性赋值
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
        //初始化视图解析器
		initViewResolvers(context);
		initFlashMapManager(context);
	}

     观察DispatcherServlet继承树里的结构 ,其继承了HttpServlet类,调用DispatcherServlet对象的Service方法时,实际调用的是HttpServlet类里的service方法。 

 在HttpServlet类里的代码中我们可以看到根据不同的前端请求方式比如GET,POST等不同的请求方式执行了不同的方法。

    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
}
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

因为是DispatcherServlet类型的对象调用的service方法,而FrameworkServlet类、HttpServlet类都实现了doGet、doPost等方法,根据动态绑定机制的原理,因为FrameworkServlet比HttpServlet类的继承管理离DispatcherServlet更近,所以调用的是FrameworkServlet里的doGet,doPost方法。在这些方法内部中我们发现都调用了processRequest方法

	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

继续分析FrameworkServlet类中的processRequest方法。

	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        //开始时间
		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
        //获取上一个LocalContext,这里叫上一个,
        //是因为此方法是从ThreadLocal中获取的,获取当前线程的LocaleContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //构建一个本地国际化上下文,SimpleLocaleContext
		LocaleContext localeContext = buildLocaleContext(request);
        //获取当前绑定到线程的RequestAttributes
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 构建ServletRequestAttributes
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        //将localeContext和requestAttributes放入当前线程中
		initContextHolders(request, localeContext, requestAttributes);

		try {
            //核心处理
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}
            //发布事件
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

在FrameworkServlet类中的该方法中调用了doService方法,但是DispatcherServlet类中存在doService方法,所以调用的是DispatcherServlet类的的doService方法。

	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}
        // 保留请求属性的快照,以防出现include,
		// 能够在include之后恢复原始属性
		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
            //请求分发
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
                    //恢复属性
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

 在上面的源码中需要重点关注doDispatch(request, response)以下是分发方法的源码

在该源码中需要关注以下代码

1.getHandler(processedRequest)   获取处理器链的方法

2.getHandlerAdapter(mappedHandler.getHandler())  获取处理器链适配器

3.mappedHandler.applyPreHandle(processedRequest, response) 请求前预处理

4.ha.handle(processedRequest, response, mappedHandler.getHandler())调用处理器的处理方法

5.mappedHandler.applyPostHandle(processedRequest, response, mv)请求执行后处理

6.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)

   请求后置处理

2.请求是如何获取到处理器执行链

本文分析的是获取处理器的过程,所以主要讲述方法一和方法二

	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 {
                //检查当前请求是不是一个文件上传的请求(),
                //是返回MultipartHttpServletRequest类型对象
				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 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 (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

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

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

2.1获取处理器执行链分析 

方法一:getHandler获取处理器

找到DispatcherServlet类里的getHandler方法,代码中handlerMappings属性中获取到HandlerMapping类型的对象。实际上该对象是RequestMappingHandlerMapping类的一个实例。

注意:为什么是有7种映射但是返回的是RequestMappingHandlerMapping的映射就不分析了。

因为这些方法一个一个分析比较麻烦。只需要知道通过debug最终返回的是一个RequestMappingHandlerMapping类型的对象就可以了。因为需要分析的类太多了。

获取到RequestMappingHandlerMapping类型的对象后,该对象的执行逻辑hm.getHandler方法来获取方法执行器链

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping hm : this.handlerMappings) {
				if (logger.isTraceEnabled()) {
					logger.trace(
							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
				}
				HandlerExecutionChain handler = hm.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

 HandlerMapping里的getHandler是一个未实现的方法,实际实现的方法在AbstractHandlerMapping类中的getHandler方法。

	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?
        // 如果返回的是handler是String类型的去Spring上线文中根据bean名获取处理器Bean对象
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
        //获取处理器执行器链
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        //处理跨域相关的执行器配置,由拦截器配置
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            //获取跨域相关的执行器链,该执行器链由拦截器实现
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

在这个方法中存在比较重要的三个方法,本文分别针对这些方法做分析

方法1:getHandlerInternal(request)

方法2:  getHandlerExecutionChain(handler, request)

方法3:getCorsHandlerExecutionChain(request, executionChain, config)

2.1.1 查找内部的处理器

    分析 getHandlerInternal方法,发现AbstractHandlerMapping类中的getHandlerInternal方法是一个抽象方法。因为AbstractHandlerMapping和AbstractHandlerMethodMapping是RequestMappingHandlerMapping的父类,所以其实现方法在AbstractHandlerMethodMapping中实现的。

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //1.获取请求的url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
            //2.查找url对应的handlerMethod
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
            //3.如果handlerMethod不为空则创建一个新的handlerMethod返回
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

关注AbstractHandlerMethodMapping类中的lookupHandlerMethod方法

	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
        //根据路径查找,在urlLookup属性中查找,该属性是一个Map,
        //该属性中的值是在register方法中赋值的,register方法的调用可以在我写的上篇文章
        //SpringMvc源码分析(二) 请求执行前的准备过程:
        //@Controller/@RequestMapping注解解析为RequestMappingInfo的过程可以看到
        //返回的类型是List<RequestMappingInfo>类型的数据
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
            //如果有匹配的结果生成一个新的RequestMappingInfo对象放入到matches对象中
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
            // 如果通过url没找到,则遍历所有的 mappings 匹配,匹配类似于 /hello/{name} 的url
            // mappings也是一个map,key是RequestMappingInfo, value是HandlerMethod
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}
        //找到最佳的匹配并返回对应的handlerMethod

		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				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();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
            //返回handerMethod
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

最佳匹配的规则如下图的源码所示:

优先级分别为:

请求方式>路径>参数等等

	protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
		return (info1, info2) -> info1.compareTo(info2, request);
	}
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
		int result;
		// Automatic vs explicit HTTP HEAD mapping
		if (HttpMethod.HEAD.matches(request.getMethod())) {
			result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
			if (result != 0) {
				return result;
			}
		}
		result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.producesCondition.compareTo(other.getProducesCondition(), request);
		if (result != 0) {
			return result;
		}
		// Implicit (no method) vs explicit HTTP method mappings
		result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
		if (result != 0) {
			return result;
		}
		return 0;
	}

此时我们就找到了该请求对应的MethodHandler了。

2.1.2 获取处理器执行链 

在AbstractHandlerMapping类 getHandler(HttpServletRequest request)方法中调用的getHandlerExecutionChain(handler, request)方法通过handler和request获取到了该请求的执行器链。分析getHandlerExecutionChain方法。

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        //获取请求路径
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                // 判断当前请求路径是否满足interceptor里配置的路径
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

1. Interceptors:用于配置SpringMVC的拦截器,有两种设置方式: 通过子类的extendInterceptors钩子方法进行设置 ;通过子类的extendInterceptors钩子方法进行设置。Interceptors并不会直接使用,而是通过initInterceptors方法按类型分配到mappedInterceptors和adaptedInterceptors中进行使用,Interceptors只用于配置。
2. mappedInterceptors:此类Interceptor在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecutionChain里。它有两种获取途径:从interceptors获取或者注册到spring的容器中通过detectMappedInterceptors方法获取。
3. adaptedInterceptors:这种类型的Inerceptor不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。它只能从interceptor中获取。
AbstractHandlerMapping的创建其实就是初始化这三个Interceptor

在AbstractHandlerMapping类中存在initApplicationContext方法。

	protected void initApplicationContext() throws BeansException {
        //扩展拦截器
		extendInterceptors(this.interceptors);
        //在adaptedInterceptors容器中加入MappedInterceptor类型的拦截器
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}

在上面的源码中

1. extendInterceptors是模板方法,用于给子类提供一个添加(或者修改)Interceptors的入口,不过在现有springMVC的实现中并没有使用。
2. detectMappedInterceptors方法用于将SpringMVC容器及父容器中的所有MappedInterceptor类型的Bean添加到mappedInterceptors属性。
3. initInterceptors方法的作用是初始化Interceptor,具体内容其实是interceptors属性里所包含的对象按类型添加到mappedInterceptors或者adaptedInterceptors:

DelegatingWebMvcConfiguration类中的方法可以通过实现WebMvcConfigurer中的addInterceptors方法并往其中添加实现HandlerInterceptor接口的拦截器,然后将实现WebMvcConfigurer接口的类纳入Spring容器管理来实现自定义拦截器

源码一:往WebMvcConfigurerComposite里的delegate添加自定义拦截器

	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}

 源码二:往registry添加从delegate取到的自定义拦截器

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.addInterceptors(registry);
		}
	}

 

2.1.3获取跨域处理执行链

 AbstractHandlerMapping类中的getCorsHandlerExecutionChain方法的作用是通过读取跨域配置往拦截器链中添加拦截器

protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
			HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

		if (CorsUtils.isPreFlightRequest(request)) {
			HandlerInterceptor[] interceptors = chain.getInterceptors();
			chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
		}
		else {
            //添加跨域拦截器
			chain.addInterceptor(new CorsInterceptor(config));
		}
		return chain;
	}

2.2封装处理器链适配器

方法二:getHandlerAdapter(mappedHandler.getHandler())  获取处理器适配器。

	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 + "]");
				}
                //判断适配器是否支持该方法处理器,此处符合要求的适配器是
                //RequestMappingHandlerAdapter
				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");
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE】博客前端

目录 一、列表页 1.1导航条 1.2主题区域 1.2.1个人信息框 1.2.2 内容区 二、登录页 三、详情页 一、列表页 整体布局如下&#xff1a; 1.1导航条 导航条分为三块&#xff0c;整体都设置id为导航栏&#xff0c;然后左右分为导航栏左和导航栏右。左边靠左&#xff0c;右边靠…

计算机视觉Computer Vision课程学习笔记四之Region and Edge Descriptions

第四章讲了区域和边界的描述 包括最佳区域评估方法&#xff0c;多物体识别&#xff0c;标签算法&#xff0c;斑点标记 以及矩评估的方法和优劣 Region Description Simple measurements on binary images • Use for recognition, etc. • Generate region descriptions whic…

Win10+CMake+VS2017编译OpenCV4.5.5

第一步&#xff1a;准备工作1 下载opencv4.5.5下载OpenCV4.5.5&#xff0c;并解压到自己新建文件夹opencv下。2 下载opencv_contrib4.5.5下载opencv_contrib4.5.5&#xff0c;解压到上面的opencv文件夹中&#xff0c;并在opencv文件夹中新建一个build文件夹&#xff0c;用来存放…

第一天总结 之 用户管理界面的实现 之 添加操作 的实现

添加操作的实现 明确页面的跳转 找到 admin_adduser.jsp中 form表单 前端的添加页面展示 在表单中输入 信息 点击注册跳转到 from表单对应的 action地址 UserAddServlet 创建UserAddServlet 从前端的form表单中获取值 然后在service层 进行 业务操作 即将这些属性存放在 Ob…

私有部署与SaaS模式网站有什么区别

什么是SaaS SaaS 是 Software-as-a-Service 的简称&#xff0c;它是一种通过互联网提供软件的模式。 以官网为例&#xff0c;SaaS订阅的网站通常统一部署在SaaS提供商的云服务器上。用户通过自己的实际需求订购对应的网站系统服务&#xff0c;按订购的系统功能、使用流量/存储…

Word处理控件Aspose.Words功能演示:用Java从Word文档中提取文本

Aspose.Words For .NET是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c;API支持所有流行的Word…

凭记忆错题记录-

3、某种部件用在2000合计算机系统中&#xff0c;运行工作1000小时后&#xff0c;其中有4台计算机的这种部件失效&#xff0c;则该部件的千小时可靠性度R为()。 A.0.990 B.0.992 C.0.996 D.0.998 【参考答案】D  8、9、X509数字证书标准推荐使用的密码算法是(8)&#xff0c;而…

计算机视觉Computer Vision课程学习笔记八之Recognition识别 low level

第八章讲了全局图像识别的方法 距离 空间特征 简单的分类模型 Recognition (low level / global matching) Task – from a description of the image in terms of “good” features (not just blobs) extract a meaning • Detect • Classify • Etc. • Techniques – Te…

C++动态规划超详细总结

动态规划首先来介绍一下动态规划&#xff0c;但我不想用过于官方的语言来介绍。动态规划是一种思想&#xff0c;它常用于最优解问题&#xff08;即所有问题包括所有子问题的解为最优解&#xff09;&#xff0c;它有点像递推&#xff0c;是在已知问题的基础上解决其他问题。这种…

【openWrt】安装后进行定制

修改openWrt管理后台默认端口vim /etc/config/uhttpd修改如下图内容然后重启uhttpd服务即可生效etc/init.d/uhttpd restart修改openWrt软件包源可以在openwrt后台改也可以在/etc/opkg/distfeeds.conf直接改vim /etc/opkg/distfeeds.conf配置如下src/gz handsomemod_core https:…

通讯录的实现

问天下谁与争锋&#xff0c;唯我傲视苍穹 此句赠与在看文章的你 该通讯录使用的语言是C语言&#xff0c;涉及的知识有动态开辟内存&#xff0c;和文件内存管理。 动态开辟内存是用来不断给通讯录增加容量的 文件管理是用来将通讯录的信息存储到文件里。 我会先从简单的写起&am…

公司裁员70%,小组从20个人降到5个人,年底公司耍无赖,全员打绩效C,就为了不发年终奖!...

年终奖写进合同&#xff0c;公司还能耍赖不给吗&#xff1f;一位网友吐槽&#xff1a;坐标小公司&#xff0c;公司裁员70%&#xff0c;自己组从20个人降到5个人。现在年底了&#xff0c;公司耍无赖&#xff0c;全员打绩效C&#xff0c;就为了不发年终奖&#xff01;年终奖都是写…

454. 四数相加 II 383. 赎金信 15. 三数之和 18. 四数之和

454. 四数相加 II 巧用哈希表&#xff0c;哈希表键值对对应的是两数之和&#xff0c;两数之和出现次数。 首先定义 一个unordered_map&#xff0c;key放a和b两数之和&#xff0c;value 放a和b两数之和出现的次数。遍历大A和大B数组&#xff0c;统计两个数组元素之和&#xff0…

Nginx与LUA(4)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;Nginx既然可以限制流量&#xff0c;那能不能「扩展」流量呢&#xff1f;当然可以&#xff0c;但可能不是你想象的那种「扩展」&#xff0c;更准确地来说是复制&am…

KVM虚拟化基本操作

1&#xff0c;虚拟化的一些介绍 虚拟化软件是可以让一台物理主机建立与执行一至多个虚拟化环境的软件&#xff0c;虚拟化将硬件、操作系统和应用程序一同封装一个可迁移的虚拟机档案文件中。 安装位置分类 目前从Hypervisor(虚拟机管理程序)安装位置分类&#xff0c;虚拟化层…

[linux] 进程相关概念理解

文章目录1. 什么是进程管理本质的解释描述组织结论2.查看进程查看进程方法1创建终端输入命令显示进程一个程序存在多个进程查看进程方法2查看成功查看失败结论3.通过系统调用获取进程标识符1.获取PID值验证PID值是否正确2. 获取父进程PID值验证3. 父进程为什么不变化&#xff1…

Vue3实现九宫格抽奖效果

前言 好久没有写文章了&#xff0c;上一次发文还是年终总结&#xff0c;眨眼间又是一年&#xff0c;每每想多总结却是坚持不来&#xff0c;难顶。  这次分享一个九宫格抽奖小游戏&#xff0c;缘起是最近公司内部做积分抽奖需求&#xff0c;抽出其中抽奖动效做一个总结&#x…

利用steam搬砖信息差项目,投入不到1万,一个月净赚3万+

老实说&#xff1a;我在做之前没有任何经验&#xff0c;但做梦也没想到&#xff0c;刚开始操作收益就远远超出我的预期&#xff01; 这个账号我才运营了一个月左右&#xff0c;就有3万多的销售额。现在我每月的收入都在上万元&#xff0c;而且随着收益越来越多&#xff0c;操作…

亚马逊云科技re:Invent引领云计算未来方向

亚马逊云科技合作伙伴网络大使计划&#xff0c;吸纳拥有多项亚马逊云科技认证和深入亚马逊云科技知识的合作伙伴成员&#xff0c;协助其成为各个领域的技术专家&#xff0c;开发可供公众使用的内容&#xff0c;如技术写作、博客、开源项目&#xff0c;宣传亚马逊云科技及其合作…

JavaWeb基础(四) JSP介绍

JavaWeb基础(四) JSP介绍 1&#xff0c;JSP 概述 JSP&#xff08;全称&#xff1a;Java Server Pages&#xff09;&#xff1a;Java 服务端页面。是一种动态的网页技术&#xff0c;其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义 Java代码的动态内容&#xf…