springmvc源码分析

说明

我们知道SoringMvc的需要创建一个DispatcherServlet,说白了他就是一个Servlet,那么我们知道Servlet的生命周期是这样的.
首先tomcat容器会为我们创建Servlet对象,当我们第一次访问Servlet的时候会调用Servlet的inti() 方法,然后访问service()方法,在Service方法最终决定使用doGet还是doPost方法来处理请求,之后的每次请求都会访问Service方法,当容器销毁的时候调用destory()方法:
并且我们知道Servlet的实现方式,一个实现两个继承,实现Servlet接口,继承HttpServlet抽象类,继承GenericServlet抽象类。

步骤

既然流程知道了,那么我们就可以根据Servlet的生命周期,来一步步的了解SpringMVC的源码,

Servlet初始化

首先找到Init()方法:
在这里插入图片描述
可以看出在DispatcherServlet类中并没有相应的初始化方法,只有一些参数信息,所以我们继续往上找。
在这里插入图片描述FrameworkServlet里面还是没有初始化方法,我们继续往上走,
在这里插入图片描述在这里插入图片描述找到了,在这里,可以看出他首先创建了一个Servlet的配置对象,就是去获取我们配置在Servlet的初始化参数,init parmeters 。
最后会执行initServletBean()方法,

@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		long startTime = System.currentTimeMillis();

		try {
			this.webApplicationContext = initWebApplicationContext();
			可以看出这里初始化了Spring容器。并且创建的ApplicationContext对象是WebApplicationContext对象(就是一个Spring容器)。
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}

initFrameworkServlet()继续初始化,
在这里插入图片描述可以看出在子类中没有对这个方法进行重写,所以结束了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一些初始化的方法。

service方法

我们知道当前请求进来的时候会访问Service方法,一步步去找,
在这里插入图片描述可以看出FrameworkServlet中重写了Service方法(主要是为了处理请求方式为PATCH的请求一个没有请求的方式的情况),

关于PATCH请求参考:
链接: https://blog.csdn.net/cdfukaifeng/article/details/80882096.

所以主要是还是去执行HttpServlet中的service方法,这个方法我们知道他是根据请求的类型去执行对应DoXXX方法,这里以DoPost方法为例子:
在这里插入图片描述可以看出在FrameworkServlet中重写了所有的DoXXX方法,最终也是去执行processRequest方法,

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		获取以前的Context上下文
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		创建一个新的上下文对象
		LocaleContext localeContext = buildLocaleContext(request);
		获得之前的请求属性
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		创建新的请求属性对象
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		
		主要就是这里,创建一个新的上下文对象,是为了符合springMVC吧应该是
		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 {
			//哦 上面获得以前的上下文是为了保留现场,在请求执行完毕之后可以还原回去,不然Tomcat容器可不认识你的添加了一下
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

主要方法执行doService 方法,改变了上下文之后执行doService方法,

DoService方法

可以看出DoService方法在FrameworkServlet中只是定义了一个抽象方法,所以到子类DispatcherServlet中查看:

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);//日志相关的东西

		// 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);看出主要是执行这个方法,在这个方法的前后是保存了request 中的某些信息。

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.
				//1 、先去获得
				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 (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);
				}
			}
		}
	}

可以看出步骤是这样的:
1、先去获得mappedHandler 对象,(通过url查找到对应的处理器,就是我们的controller中的方法),如果没有则返回了,如果有的话,那么就会把找到的handler放到处理适配器中。
2、把处理器放到处理适配器(HandlerAdapter)中。
在这里插入图片描述如上就是一个Handler的一些信息,
最终执行方法是通过适配器来执行的,
3、执行拦截器代码:
在这里插入图片描述执行的是拦截器的preHandle 方法,如果有一个拦截器返回了false就会返回false,
4、适配器执行实际的方法,
在这里插入图片描述
ha的实际的类型:
在这里插入图片描述
在这里插入图片描述查看实际的handle方法:
在这里插入图片描述

@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;
	}

可以看出方法中,先执行了invokeHandlerMethod 方法,最后返回对应的modelAndView对象,

具体执行controller中的方法

@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

返回值处理器:
在这里插入图片描述ModelAndViewContainer在这里里面添加了,请求数据,
在这里插入图片描述初始化之后():
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

@Nullable
	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
	}

上面的方法就是得到ModelAndView对象。

执行对应的方法,返回ModelAndView对象。
最后返回到doDispatch 的ModelView对象为:
在这里插入图片描述执行完毕控制器之后,也返回了VIewAndModel,接下来(如果方法没有返回对应的View则创建默认的View)
在这里插入图片描述在这里插入图片描述可以看出当返回值为Void的时候会创建默认的View。

最后执行:
在这里插入图片描述processDispatchResult(开始处理返回值,或许我们已经知道了,最后需要通过视图解析器渲染并返回视图)。
进入到processDispatchResult 方法中我们可以看出,
在这里插入图片描述传递过来的ModelAndView对象如果不是空,并且:
在这里插入图片描述就会开始渲染视图了,

render方法:

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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// We need to resolve the view name.
			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() + "'");
			}
		}
		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.isTraceEnabled()) {
			logger.trace("Rendering view [" + view + "] ");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "]", ex);
			}
			throw ex;
		}
	}

在这里插入图片描述
创建View对象,
在这里插入图片描述这里就会去遍历,所有的视图解析器,然后
默认是有这么一个视图解析器的:
在这里插入图片描述InternalResourceView,
在这里插入图片描述
具体就是执行制图解析器中的resolveViewName() 方法,当视图解析器解析之后,就会返回一个View对象。

通过View渲染数据,然后返回到前端。
在这里插入图片描述view.render(mv.getModelInternal(), request, response);渲染数据。

就是说实际最后是使用View对象来进行渲染的。
在这里插入图片描述

总结

总结SpringMVC的流程就是,当请求进来之后,会先通过遍历HandlerMappins找到对应处理逻辑的Handler,然后把Handler放到对应的HandlerAdapter 中通过,适配器来执行,返回一个ModelAndView对象,这里面主要包括的是ViewName和Model,然后是遍历视图解析器(视图解析器是在初始化的时候就放进去了的,最上面有),视图解析器通过对ViewName进行解析,得到了VIew对象,然后使用View对象进行渲染,返回给前端。

参考文章: https://www.cnblogs.com/panxinqi/p/6647464.html.
参考文章: https://blog.csdn.net/u013219087/article/details/80649649.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值