Spring-21 SpringMVC 处理器适配器 看懂了你也能写框架

63 阅读4分钟

Spring-21 SpringMVC 处理器适配

Spring 源码系列文章会遵循由浅入深,由易到难,由宏观到微观的原则,目标是尽量降低学习难度,而不是一上来就迷失在源码当中. 文章会从一个场景作为出发点,针对性的目的性极强的针对该场景对 Spring 的实现原理,源码进行探究学习。该系列文章会让你收获什么? 从对 Spring 的使用者成为 Spring 专家。该文章会同步在微信公众号 【DevXTalk】, 方便在微信客户端阅读。

开门见山的说一些读者可能会想过, 我的发(What F*ck)我就只想发起一个请求,处理这个请求就完事了,框架给我搞的那么复杂干嘛?难道框架作者都是吃饱了撑的是吧!记住人家写的代码是面向全世界范围的广大群体,要考虑到对各种使用场景的支持。所以代码就必须灵活,要非常方便扩展,要不然有人敢用么?

这篇内容重点探讨 Spring MVC 中的 HandlerAdapter 处理器适配器.重点不在于对它的实现源码研究的多深入细致,而是培养设计思维和架构思维。

HandlerAdapter 接口定义

首先我们需要明确的是 HandlerAdapter 处理器适配器它服务的目标是 处理器 , 它是为了适配不同的处理器而存在的。因为框架需要能够灵活的扩展,处理器的形式是不固定的,可能会有很多种处理器,所以在调用处理器进行处理请求的位置代码不能被写死(不经过修改就不能改变程序的行为)。

HandlerAdapter 两个核心 API supports 判断当前适配器支不支持传入的 Handler 处理器。handle 处理请求,handle 方法的第三个参数是 Object handler 这个 handler 才是真正处理请求的。这里 handler 的参数类型是 Object 可以接收任意类型的对象非常的灵活,当然灵活带来的一个弊端就是风险, 势必要在编程时遵守某些规范。

public interface HandlerAdapter {

    boolean supports(Object handler);
    
    @Nullable  
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

适配

image.png

首先要理解什么是适配,适配就是在两个已经存在的事物中间起到衔接作用,目的是让这两个事物可以协同工作。所以适配器一定是个中间组件,比如手机充电器的转接头,一端连着充电器,另一端连着手机。再比如一个内向的人在一个外向型的团体中,为了融入团体,他会刻意表现的偏外向一些,也可以把适配理解成一种伪装吧。

Spring MVCHandlerAdapter 处理器适配器连接的两端是 DispatcherServlet 和 处理器。DispatcherServlet 是不知道处理器具体是什么类型,如何工作的, 它只需要和 HandlerAdapter 交互即可。

下图是 Spring MVCHandlerAdapter 的结构。DispatcherServletHandlerAdapter 之间遵循着一套接口规范,处理器是各式各样的。当把这些不同的处理器伪装成 HandlerAdapter 之后,它们就可以和 DispatcherServlet 协同工作了。

内置适配器

介绍一些 Spring MVC 中已经内置的适配器。

HttpRequestHandlerAdapter 支持的处理器是 HttpRequestHandler .

@FunctionalInterface
public interface HttpRequestHandler {

	/**
	 * Process the given request, generating a response.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws ServletException in case of general errors
	 * @throws IOException in case of I/O errors
	 */
	void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException;

}

SimpleControllerHandlerAdapter 支持的处理器是 Controller .

@FunctionalInterface
public interface Controller {

	/**
	 * Process the request and return a ModelAndView object which the DispatcherServlet
	 * will render. A {@code null} return value is not an error: it indicates that
	 * this object completed request processing itself and that there is therefore no
	 * ModelAndView to render.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @return a ModelAndView to render, or {@code null} if handled directly
	 * @throws Exception in case of errors
	 */
	@Nullable
	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

}

SimpleServletHandlerAdapter 支持的处理器是 Servlet .

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

HandlerFunctionAdapter 支持的处理器是 HandlerFunction .

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {

	/**
	 * Handle the given request.
	 * @param request the request to handle
	 * @return the response
	 */
	T handle(ServerRequest request) throws Exception;

}

开发中最常用的 RequestMappingHandlerAdapter 支持的处理器是 HandlerMethod .

	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// 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) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

调用处理器的位置 org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

通过反射的方式调用了处理器方法

	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		Method method = getBridgedMethod();
		try {
			if (KotlinDetector.isSuspendingFunction(method)) {
				return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
			}
			return method.invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(method, getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}

DevX 会持续分享有趣的技术和见闻,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXTalk】, 方便在微信客户端阅读。