深入SpringMVC一次请求的执行原理

引言

作者采用的是springMVC xml的方式进行的学习。
springMVC请求映射支持多种注解类型,当你在类上或方法上使用一个注解就可以达到相应的功能,这不得不让人惊叹这其中的神奇!在写这篇文章时,我参考了很多博客,通过idea一调试就是一天,希望能通过我自己的理解能给你带来帮助,本人能力水平有限,若有错误之处,还请指出。

请求流程

自己画的,有点小丑,但问题不大,先有个请求流程概念,再阅读源码
在这里插入图片描述
1.首先用户通过浏览器请求到达了springMVC DispatchServlet核心类(底层是通过servlet调用)
2.通过request对象查找合适的处理器
3.拿到处理器后获得controller对象 根据该对象去寻找合适的处理适配器
4.然后进行数据封装一系列业务逻辑
5.封装完成后拿到封装的对象数组 进行反射调用controller方法
6.执行完controller方法后返回modelAndView (数据和视图)
7.再对根据视图解析得到对应的页面 比如是jsp还是thymeleaf
8.最后得到页面返回给浏览器

策略对象初始化

因为我们从前端请求过来的需求有多种多样,那么后台该如何应对多样业务或数据进行区分呢?
springMVC提供了一个业务接口和多种实现类策略对象这种方式,用来适用对应的业务,进行相应的业务逻辑处理。
springMVC通过遍历所有的策略对象来适配策略,只要按顺序满足其中一种策略便结束查找,若没有查找到则抛出异常 (图不是很标准,大家了解大概意思)
在这里插入图片描述

在启动服务器时会调用initStrategies方法,初始化策略对象。

public class DispatcherServlet extends FrameworkServlet {
	/**
	 //初始化这个servlet使用的策略对象。
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	 //用来对后面请求操作提供策略对象
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		//初始化映射策略对象
		initHandlerMappings(context);
		//初始化controller对象适配器策略对象
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		//初始化视图解析策略对象
		initViewResolvers(context);
		initFlashMapManager(context);
	}
}

核心doDispatch方法

该方法是DispatchServlet中的重要核心方法,他将协调一次请求的所有业务逻辑,后续的知识点将对这个方法进行细分,可以先对这个方法执行了那些业务做一个概括的了解。

public class DispatcherServlet extends FrameworkServlet {
	/**
	 * 获得相应的策略进行相应的业务逻辑处理
	 * 按顺序来获得适配的Handler处理器
	 * HandlerAdapter将通过处理器对象适配获得
	 * 只能获得一个
	 * 所有的http方法都将被这个方法处理,由handleradapter或handlers本身决定哪些方法是可适配的。
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	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 {
				//如果提交的是Multipart请求方式 内部采取对应的方式进行解析
				//若不是 request不变
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 将request传递 来获取对应的处理器(请求适配)
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 将处理器中的handler对象传递进去 获取到对应的适配器(对象适配)
				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;
					}
				}
				//执行拦截器PreHandle方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					//PreHandle方法要是返回false 内部依旧会执行拦截器的afterCompletion方法
					//最终结束请求
					return;
				}

				// Actually invoke the handler.
				//底层通过反射去调用controller的方法 然后将数据和视图返回给mv
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				
				//如果没有定义视图名将采用默认的视图名
				applyDefaultViewName(processedRequest, mv);
				//执行拦截器的PostHandle方法
				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) {
			//执行拦截器中的afterCompletion方法
			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);
				}
			}
		}
	}
}

接下来,我们来对doDispatch这个方法执行的业务进行解剖

1.获取执行链对象

	mappedHandler = getHandler(processedRequest);

通过request对象去获取适合本身的处理器

public class DispatcherServlet extends FrameworkServlet {
	/** List of HandlerMappings used by this servlet. */
	//在服务器启动时已经进行初始化
	@Nullable
	private List<HandlerMapping> handlerMappings;
	
	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order. 按顺序尝试所有处理程序映射
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			//遍历集合中所有HandlerMapping的实现类(策略对象)
			for (HandlerMapping mapping : this.handlerMappings) {
				//通过request对象来适配合适的处理器
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					//只要有一个策略满足就返回
					return handler;
				}
			}
		}
		return null;
	}
}

拿到的HandlerExecutionChain 对象,我们看注释,可以知道这个类是由我们当前的处理器对象和多个拦截器组成的,包括我们自定义的拦截器都会添加到这个对象中

/**
    //处理器执行链,由对象和和多个拦截器组成
 * Handler execution chain, consisting of handler object and any handler interceptors.
 * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
 */
public class HandlerExecutionChain {
	
	//在构造方法中被赋值为controller对象
	private final Object handler;

    //拦截器对象
	@Nullable
	private HandlerInterceptor[] interceptors;

	@Nullable
	private List<HandlerInterceptor> interceptorList;
	
	//会调用拦截器preHandle方法
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception
	
	//会调用拦截器postHandle方法
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
	
	//会调用拦截器afterCompletion方法
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
		
	.....省略
}

2.获取适配器

	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

通过我们前面拿到的HandlerExecutionChain对象,将handler对象传递进行,获取到对应的策略处理适配器

public class DispatcherServlet extends FrameworkServlet {
	/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	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");
	}
}

3.数据封装

在了解数据封装之前,我们得知道这个接口,所有的数据解析封装策略都是基于该接口去实现的,我们可以看到有两个方法,我们简单了解一下

public interface HandlerMethodArgumentResolver {

	//true该策略支持该参数
    boolean supportsParameter(MethodParameter var1);

	//当supportsParameter方法返回true 便会执行这个方法进行数据解析封装
    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

下面大多是数据解析的策略对象,都是通过实现HandlerMethodArgumentResolver接口,我们也可以自己实现接口来自定义一个数据解析的策略对象
在这里插入图片描述
回过头来,我们再来看我们的源代码。

	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

在找到适配器后,执行适配器的handle方法直到调用InvocableHandlerMethod类的invokeForRequest方法进行数据封装,最终通过反射调用controller的方法并拿到数据和视图赋值给mv

public class InvocableHandlerMethod extends HandlerMethod {

	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	    //getMethodArgumentValues方法进行数据封装
	    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
	    if (this.logger.isTraceEnabled()) {
	        this.logger.trace("Arguments: " + Arrays.toString(args));
	    }
	    //内部通过反射调用controller方法 并将封装好的参数传递过去
	    return this.doInvoke(args);
	}

	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	    //获得controller方法入参个数
	    MethodParameter[] parameters = this.getMethodParameters();
	    if (ObjectUtils.isEmpty(parameters)) {
	    	//如果为空直接返回空数组
	        return EMPTY_ARGS;
	    } else {
	        //创建对应参数长度的数组
	        Object[] args = new Object[parameters.length];
	        for(int i = 0; i < parameters.length; ++i) {
	            MethodParameter parameter = parameters[i]
	            if (args[i] == null) {
	                if (!this.resolvers.supportsParameter(parameter)) {
	                    //没有找到该参数的解析器时抛出异常
	                    throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
	                }
	                try {
	                    //通过调用HandlerMethodArgumentResolver接口实现类的resolveArgument方法()寻找支持该参数类型的解析器进行数据封装
	                    //并将参数传递进去
	                    args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
	                } catch (Exception var10) {
	                    throw var10;
	                }
	            }
	        }
	        //封装完后,返回封装数据
	        return args;
	    }
	}
}

HandlerMethodArgumentResolverComposite 类是数据封装协调的核心类,采用了设计模式的组合模式,对不同的参数注解 比如我们常用的@RequestParam @ReuqestBody等 采取不同的执行策略

//寻找合适的数据解析器核心类
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	//用来存放默认数据解析类
    private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList();
    //存放已经解析过的参数,加快查找过程
    private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap(256);
   

	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	    //getArgumentResolver()方法传递参数来获得适合的解析器
	    HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
	    if (resolver == null) {
	        //找不到的话抛出异常
	        throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
	    } else {
	        //然后调用当前获取到的解析器的resolveArgument()方法来进行数据解析封装处理
	        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	    }
	}

	@Nullable
	//查找对应的参数解析器
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	    //先从集合读取对应的解析器,如果上一次读取过则可以直接读取到
	    HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
	    if (result == null) {
	        //没有在集合中找到对应的解析器
	        //迭代所有的解析器
	        Iterator var3 = this.argumentResolvers.iterator();
	
	        while(var3.hasNext()) {
	            HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
	            if (resolver.supportsParameter(parameter)) {
	                //找到支持的数据解析器
	                result = resolver;
	                //添加到集合中,方便下次查找,直接取出
	                this.argumentResolverCache.put(parameter, resolver);
	                break;
	            }
	        }
	    }
	    //没有查到找则返回空
	    return result;
    }
}

我们来对支持@ReuqestParam 注解的策略supportsParameter方法简单讲解一下,方便大家了解

public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {

	public boolean supportsParameter(MethodParameter parameter) {
		//可以简单的看出 只要参数上注解了@RequestParam 便会返回true 表示支持该类型
        RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
        return requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name());
    }

	....省略业务逻辑方法
    
 }

4.反射调用

前面已经对数据解析和封装的执行原理进行了讲解,数据正确封装后将通过反射调用controller方法

public class InvocableHandlerMethod extends HandlerMethod {

	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	    //getMethodArgumentValues方法进行数据封装
	    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
	    if (this.logger.isTraceEnabled()) {
	        this.logger.trace("Arguments: " + Arrays.toString(args));
	    }
	    //内部通过反射调用controller方法 并将封装好的参数传递过去
	    return this.doInvoke(args);
	}
}

5.ModelAndView

	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

反射调用完后,会将view和model返回给mv;

6.视图解析

	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

视图解析同样也有着不同的处理策略,比如是jsp的视图解析还是thymeleaf的视图解析。

public class DispatcherServlet extends FrameworkServlet {

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
				@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
				@Nullable Exception exception) throws Exception {
	
		boolean errorView = false;

		if (exception != null) {
			//有异常则向前端返回错误页面
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				//异常解析
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// 视图不为空的情况下
		if (mv != null && !mv.wasCleared()) {
			//视图解析方法
			render(mv, request, response);
			if (errorView) {
				//清理异常错误信息
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			//执行拦截的AfterCompletion方法
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
}

视图解析方法,方便理解,只保留了本内容重要代码

public class DispatcherServlet extends FrameworkServlet {

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	
		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			//根据视图名 采取不同策略进行解析
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			//视图渲染 看下一标题内容
			view.render(mv.getModelInternal(), request, response);
		}
	}

	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {

		if (this.viewResolvers != null) {
			for (ViewResolver viewResolver : this.viewResolvers) {
				//如果你在xml中配置了视图解析器 这时就会调用他进行视图解析
				View view = viewResolver.resolveViewName(viewName, locale);
				if (view != null) {
					//解析完成返回view
					return view;
				}
			}
		}
		return null;
	}
}

获得相应的视图解析策略 博主使用的是jsp页面 这里获取的是自定义实现的internalResourceView对象
进行的视图解析

实现ViewResolver 接口的大概策略,博主没有对策略具体源码进行过研究,就不带大家去探讨了,有兴趣的小伙伴可以去了解
在这里插入图片描述

7.视图渲染

获取到解析的view对象后 准备进行视图渲染转发 因为这个InternalResourceView类没有重写父类的render方法所以这里使用的是父类AbstractView类的render方法

public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {

	public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		//将model中的数据写入到mergedModel集合中
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		//当通过HTTPS发送下载内容时,默认实现为IE漏洞提供了一个解决方案。
		prepareResponse(request, response);
		//页面转发
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}
}
	
public class InternalResourceView extends AbstractUrlBasedView {

	/**
	 * Render the internal resource given the specified model.
	 * This includes setting the model as request attributes.
	 */
	@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// model中的数据写入到request域中
		exposeModelAsRequestAttributes(model, request);
		// Expose helpers as request attributes, if any.
		exposeHelpers(request);
		// 获得转发路径
		String dispatcherPath = prepareForRendering(request, response);
		// 获取目标资源(通常是JSP)的RequestDispatcher。
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		//判断是否是include
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including [" + getUrl() + "]");
			}
			rd.include(request, response);
		}
		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to [" + getUrl() + "]");
			}
			//请求转发 视图渲染
			rd.forward(request, response);
		}
	}
}

总结

sprinMVC的请求核心类是DispatchServlet,其中的doDispatch是核心方法
在一次请求中

  1. 首先会对request请求方式进行解析
  2. 然后通过request对象获得的映射处理器
  3. 通过处理器获得handler对象,根据对象获得对象适配器
  4. 然后执行拦截器的prehandle方法 如果返回false 执行拦截器afterCompletion方法 结束请求
  5. 若返回true再对 则执行适配器的hanle方法
  6. 首先查找参数适配策略进行数据封装成一个对象数组
  7. 封装完毕后通过反射将对象数组一齐传递调用controller方法
  8. 然后回调到doDispatch方法 将controller方法中返回的视图名和数据封装给modelview
  9. 下一步执行拦截器的posthanle方法
  10. 执行后 对modelview选择对应策略进行视图解析
  11. 获得解析后的view对象 将model数据写入request域中 进行页面渲染
  12. 执行拦截器afterCompletion方法后 最后响应页面

spring的思想博主了解的还是只是皮毛,本篇文章可能存在误解,还请大家指出,一起进步。
天赋不够,汗水来凑,加油!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值