SpringMVC执行图解和源码分析

一、什么是MVC
首先,大家应该都知道什么是mvc了吧, 这个应该就不需要过多的介绍了。不太了解的可以看一下这个详细的介绍。 点击打开链接
二、Spring MVC运行路程图



三、运行流程
  • 客户端请求,如果匹配DispatcherServlet属性的路径(web.xml中设置),把请求交给DispatcherServlet处理。
  • 进入DispacherServlet的doDispatch方法,如下图:
	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 || mappedHandler.getHandler() == 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()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

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

				try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
  • 首先17行,通过getHandler方法获得一个HandlerExecutionChain
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		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获得一个HandlerExecutionChain对象,这个HandlerMapping就是定义了一个请求到处理器之间的映射

可以看到,如果已经设置
	<mvc:default-servlet-handler/>

该标签的话,但是没有找到合适的处理器,springMVC会认为该请求是要请求一个静态资源。那么如果美欧配置该标签的话标签的话,那么12行,handler返回null,对应的HandlerExecutionChain也为null,返回错误404页面。
然后紧接着23行,获取了一个HandlerAdapter对象,HandlerAdapter这个对象里面包含了表单的数据绑定、数据的转换和格式化。
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		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");
	}



图为HandlerAdapter中数据的的转换、和格式化。还有校验等等......如果有错误的话,会把这些错误放在BindingResult里面。
  • 接着39行,调用拦截器的preHandle方法,如果自定义的拦截器里面preHandle方法返回false,那么后续的目标方法等。都不会被执行。
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (getInterceptors() != null) {
			for (int i = 0; i < getInterceptors().length; i++) {
				HandlerInterceptor interceptor = getInterceptors()[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
  • 可以看到45行执行了目标方法,返回了一个ModelAndView对象。

里面就包含了返回的逻辑视图,和model对象。

  • 54行,继续执行拦截器的postHandle方法
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
		if (getInterceptors() == null) {
			return;
		}
		for (int i = getInterceptors().length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
这个方法是倒序执行的,会获得所有的拦截器,包括自己自定义的拦截器,看到5行,getInterceptors().length - 1
  • 59行, 处理视图,首先先查看是否有异常
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

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

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
看到6行, 如果异常不等于空的话, 使用异常解析器开始处理异常,如果没有异常的话, 那么就开始20行,去渲染视图
	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);


		View view;
		if (mv.isReference()) {
			// We need to resolve the view name.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException(
						"Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" +
								getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}


		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		try {
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '"
						+ getServletName() + "'", ex);
			}
			throw ex;
		}
	}
10行,开始解析视图


由于我在配置文件中使用的是InternalResourceViewResolver,所以当前视图解析器是InternalResourceViewResolver,接着32行去渲染视图

	@Override
	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
				" and static attributes " + this.staticAttributes);
		}

		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);

		prepareResponse(request, response);
		renderMergedOutputModel(mergedModel, request, response);
	}

看到调用了renderMergedOutputModel(mergedModel, request, response);继续往下执行:

	@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Determine which request handle to expose to the RequestDispatcher.
		HttpServletRequest requestToExpose = getRequestToExpose(request);

		// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, requestToExpose);

		// Expose helpers as request attributes, if any.
		exposeHelpers(requestToExpose);

		// Determine the path for the request dispatcher.
		String dispatcherPath = prepareForRendering(requestToExpose, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(requestToExpose, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(requestToExpose, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.forward(requestToExpose, response);
		}
	}
找到对应的视图,看到最后盗用了rd.forward(requestToExpose, response);方法将页面进行了了转发。

  • 最后调用了拦截器的afterCompletion方法完成本次请求
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		if (getInterceptors() == null) {
			return;
		}
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
这就是我理解的springmvc执行流程,如果有什么不对的地方,还请各位批评指正,谢谢!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring MVC 框架的源码结构非常复杂,主要分成以下几个模块: 1. spring-webmvc:Spring MVC 核心模块,提供了 DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver 等核心组件的实现。 2. spring-web:Spring Web 模块,提供了 Web 相关的工具类和 Web 容器的集成支持。 3. spring-beans:Spring IoC 容器核心模块,提供了 BeanFactory、ApplicationContext、BeanDefinition 等核心组件的实现。 4. spring-context:Spring 上下文模块,提供了 Spring IoC 容器的上下文支持,包括资源加载、事件发布、应用上下文等功能。 5. spring-aop:Spring AOP 模块,提供了面向切面编程的支持。 6. spring-expression:Spring 表达式语言模块,提供了 SpEL(Spring Expression Language)表达式语言的支持。 下面是 Spring MVC 的源码结构图解: ![springmvc源码结构图解](https://img-blog.csdn.net/2018051717101490) ### 回答2: SpringMVC 是一种基于 Java 的Web应用框架,它采用了MVC(Model-View-Controller)架构模式,使得开发者可以更加方便地开发和管理Web应用。SpringMVC源码图解可以帮助我们更好地理解框架的工作原理和内部机制。 首先,SpringMVC源码中包含了核心组件,如DispatcherServlet、HandlerMapping、HandlerAdapter、HandlerInterceptor等。DispatcherServlet是整个框架的核心,它负责接收所有的HTTP请求并进行分发处理。HandlerMapping则负责根据请求的URL找到对应的Controller方法。HandlerAdapter则用于调用Controller方法并处理请求和响应。HandlerInterceptor则可以对请求进行拦截和处理。 源码图解中可以看到,DispatcherServlet首先根据请求的URL找到对应的HandlerMapping,然后将请求转发给对应的Controller方法。在Controller方法中,我们可以编写业务逻辑代码,并返回相应的数据或视图。当Controller方法返回数据时,HandlerAdapter会将数据包装成JSON或XML格式,并返回给客户端。当Controller方法返回视图时,会根据视图配置返回对应的HTML页面。 在源码图解中,我们还可以看到SpringMVC的配置文件,如web.xml和spring-mvc.xml。web.xml文件是整个Web应用的入口,其中配置了DispatcherServlet。spring-mvc.xml文件则配置了一些框架相关的组件和配置项,如视图解析器、文件上传处理器、国际化资源等。 通过对SpringMVC源码图解,我们可以更加深入地了解框架的实现原理,掌握其工作流程和关键组件的相互关系。这对于我们开发基于SpringMVCWeb应用来说非常有帮助,可以更加高效地利用框架的功能和特性,提升开发效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值