SpringMVC的工作流程和源码分析

SpringMVC框架是一个基于请求驱动的Web框架,并且使用了‘前端控制器’模型来进行设计,再根据‘请求映射规则’分发给相应的页面控制器进行处理。

整体流程

具体步骤:

1、  首先用户发送请求到前端控制器,前端控制器根据请求信息(如 URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;图中的 1、2 步骤;

2、  页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在 Spring Web MVC 中叫命令对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个 ModelAndView(模型数据和逻辑视图名);图中的 3、4、5 步骤;

3、  前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;图中的步骤 6、7;

4、  前端控制器再次收回控制权,将响应返回给用户,图中的步骤 8;至此整个结束。

 

核心流程

具体步骤:

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)

第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)

第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)

第十一步:前端控制器向用户响应结果

 

总结 核心开发步骤

1、  DispatcherServlet 在 web.xml 中的部署描述,从而拦截请求到 Spring Web MVC

2、  HandlerMapping 的配置,从而将请求映射到处理器

3、  HandlerAdapter 的配置,从而支持多种类型的处理器

注:处理器映射求和适配器使用纾解的话包含在了注解驱动中,不需要在单独配置

4、  ViewResolver 的配置,从而将逻辑视图名解析为具体视图技术

5、  处理器(页面控制器)的配置,从而进行功能处理 

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

 

相关组件说明

DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于 mvc 模式中的 cdispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求, dispatcherServlet 的存在降低了组件之间的耦合性。

HandlerMapping:处理器映射器(三大组件之一)

HandlerMapping 负责根据用户请求找到 Handler 即处理器, SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等

Handler:处理器

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

​​​​​​​HandlAdapter:处理器适配器(三大组件之一)

调用:处理器映射器。

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

​​​​​​​ViewResolver:视图解析器(三大组件之一)

View Resolver 负责将处理结果生成 View 视图, View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

​​​​​​​View:视图

SpringMVC 框架提供了很多的 View 视图类型的支持,包括: jstlViewfreemarkerViewpdfView等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面

​​​​​​​<mvc:annotation-driven>说明

SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。使 用 <mvc:annotation-driven> 自 动 加 载 RequestMappingHandlerMapping ( 处 理 映 射 器 ) 和RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解处理器和适配器的配置。

 

源码分析:请求参数绑定流程

 1、我们现在web.xml中找到DispatcherServlet的配置

2、在类DispatcherServlet找到doDispatch方法

// 下面这段代码,就是 处理器适配器执行处理器,也就是真正执行处理器。

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

3、然后找到HandlerAdapter接口中的handle方法

然后找到HandlerAdapter接口的实现类AbstractHandlerMethodAdapter(也就是RequestMappingHandlerAdapter父类) 

适配器内部执行处理方法(在实现类RequestMappingHandlerAdapter中实现)

4、接着进去AbstractHandlerMethodAdapter方法handle的返回值handleInternal方法,发现handleInternal是一个抽象方法。

AbstractHandlerMethodAdapter的子类RequestMappingHandlerAdapter对handleInternal方法进行了重写。

RequestMappingHandlerAdapter对handleInternal方法中调用了invokeAndHandle

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,

HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

......

invocableMethod.invokeAndHandle(webRequest, mavContainer);

     ......

}

5、接着来到类ServletInvocableHandlerMethod中的方法invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,

Object... providedArgs) throws Exception {

// 处理请求参数(将请求的参数数据,对应转换到方法形参上)

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

          ......

6、接着来到类InvocableHandlerMethod类的invokeForRequest方法

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,

Object... providedArgs) throws Exception {

// 处理请求参数转换方法

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

......

}

 

7、最后来到类InvocableHandlerMethod的方法getMethodArgumentValues

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,

Object... providedArgs) throws Exception {

//获取当前执行方法的形参个数

MethodParameter[] parameters = getMethodParameters();

    // 定义返回结果对象数组(参数对象)

Object[] args = new Object[parameters.length];

for (int i = 0; i < parameters.length; i++) {

// 当前循环处理的第i个参数(主要有形参类型信息)

MethodParameter parameter = parameters[i];

parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);

// 给形参参数赋值(providedArgs参数是list)

// Attempt to resolve a method parameter from the list of provided argument values.

args[i] = resolveProvidedArgument(parameter, providedArgs);

if (args[i] != null) {

continue;

}

if (this.argumentResolvers.supportsParameter(parameter)) {

try {

// 给形参参数赋值(简单类型、pojo类型,数组)

// 1.根据形参的类型,创建对象

// 2.将request中的参数数据,对应设置到形参上

args[i] = this.argumentResolvers.resolveArgument(

parameter, mavContainer, request, this.dataBinderFactory);

continue;

}

catch (Exception ex) {

if (logger.isDebugEnabled()) {

logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);

}

throw ex;

}

}

if (args[i] == null) {

throw new IllegalStateException("Could not resolve method parameter at index " +

parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +

": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));

}

}

return args;

 

源码分析:模型数据响应到jsp/html页面流程

1、我们继续来到类DispatcherServlet(doDispatch方法中),也就是处理执行结果

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

 

2、接着继续来到类DispatcherServlet的processDispatchResult方法,这里面进行视图渲染。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
		......
		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
// 视图渲染
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		......

 

3、紧接着来到类DispatcherServlet

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);
		......
		try {
			// 视图渲染
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			......
		}

 

4、接着来到类AbstractView类中的render的方法,将模型数据mergedModel设置到request中

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);
// 将模型数据mergedModel,设置到request中
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

 

5、接着,来到类InternalResourceView

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

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

 

6、最后来到类AbstractView,设置模型数据到request中

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
// 循环拿到模型map中所有的key/value
		for (Map.Entry<String, Object> entry : model.entrySet()) {
			String modelName = entry.getKey();
			Object modelValue = entry.getValue();
			if (modelValue != null) {
// 设置模型数据到request对象中
				request.setAttribute(modelName, modelValue);
				if (logger.isDebugEnabled()) {
					logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
							"] to request in view with name '" + getBeanName() + "'");
				}
			}
			else {
				request.removeAttribute(modelName);
				if (logger.isDebugEnabled()) {
					logger.debug("Removed model object '" + modelName +
							"' from request in view with name '" + getBeanName() + "'");
				}
			}
		}

 

参考链接:

https://www.cnblogs.com/leskang/p/6101368.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值