再学一点Spring MVC

概述

MVC模式,全称是Model-View-Controller(模型-视图-控制器)模式,它是一种软件架构模式,目标是将软件的用户界面和业务逻辑隔离,使代码具有更高的可扩展性,可复用行,可维护性以及灵活性.
MVC模式将应用程序划分成模型、视图、控制器等三层,如下图所示:
再学一点Spring MVC_第1张图片
概念解释:

  • model
  • 它是应用程序的实体部分,或者说是需要展示的数据,比如我们定义的bean,dto,vo等,也包含业务处理的bean,比如service,dao等等.
    
  • view
  • 它是指应用程序中与浏览器进行交互,展示数据的资源,web应用中我们可以认为是前台页面,比如jsp,html等等.
    
  • controller
  • 它是指我们编写的servelt,负责将用户的请求交给模型层进行处理,将model返回,用视图view渲染并展示给用户,controller层不做任何业务处理,只是view和model的中间桥梁.
    

MVC模式的优点:

  • 降低耦合性,三层之间相互独立,各司其职,相互更改不会影响
  • 利于分工合作,每层上都有不同的实现,擅长的人去开发维护
  • 利于组件的复用,多个视图可以共享一个model,提高了代码的可复用性

MVC初始化

完整的DispatcherServlet初始化流程,详见下图:
再学一点Spring MVC_第2张图片
这里我们以springboot为例进行springmvc初始化流程的分析,大家需要注意了,如果是不使用springboot的项目,前面的流程不一样,大家可以自行去百度搜索,spring项目主要是通过ContextLoaderListener中处理的.

内嵌tomcat

为什么在springboot项目,我们没有配置tomcat,但是也可以正常的服务启动,这是因为它里面内嵌了tomcat.
在这里插入图片描述

创建webServer

在AbstarctApplicationContext#onRefresh方法中的onRefresh方法中就会创建webServer
再学一点Spring MVC_第3张图片
再学一点Spring MVC_第4张图片

webServer通过ServletWebServerFacotry获得.
再学一点Spring MVC_第5张图片
看到这个方法,大家是不是觉得很熟悉,我们在tomcat的xml文件中,也可以看到baseDir,engine,connector等等,这里不是我们说的重点.

Lifecycle

再学一点Spring MVC_第6张图片

A common interface defining methods for start/stop lifecycle control.
The typical use case for this is to control asynchronous processing.
NOTE: This interface does not imply specific auto-startup semantics.
Consider implementing {SmartLifecycle} for that purpose.

Lifecycle是一个通用的定义start和stop方法的接口,典型的应用就是在控制异步线程处理上,但是它不自动触发的,需要显示的调用start和stop方法,如果想让实现的bean自动调用start和stop方法,可以参考SmartLifeCycle.
它可以定义spring容器对象的生命周期,任何spring管理对象都可以实现它,当ApplicationContext接收到start和stop信号时,遍历所有的Lifecycle的实现类,spring通过LifecycleProcessor实现这个功能.
再学一点Spring MVC_第7张图片
LifecycleProcessor继承了Lifecycle接口,同时提供了onRefresh和onClose的方法,当ApplicationContext上下文刷新或者关闭的时候,会触发上面的两个方法.

SmartLifecycle

再学一点Spring MVC_第8张图片
再学一点Spring MVC_第9张图片
SmartLifecycle继承了Lifecycle接口,同时继承了Phased接口.
容器中实现了Lifycycle的多个类如果希望有序的进行回调时,那么启动和关闭调用的顺序很重要,如果两个对象之间存在依赖关系,那么依赖方必须在依赖后执行,这里就是上面定义的Phased接口.
getPhase()返回值最小的对象优先执行,值越小,优先级越高,但是调用stop的时候正好相反,值越大,优先级越高.
ApplicationContext在onRefresh调用的时候,创建时DefaultLifecycleProcessor对象.

再学一点Spring MVC_第10张图片

调用start方法就会执行到webServer的生命周期bean的start方法中
再学一点Spring MVC_第11张图片

Tomcat启动

再学一点Spring MVC_第12张图片
到了这里就是真正的tomcat启动的流程了,容器启动后就会创建web容器上下文,并且初始化DispatchServlet.
tomcat启动完毕之后,发布容器初始化完成事件.

Servlet容器初始化

再学一点Spring MVC_第13张图片

DispatchServlet,FrameworkServlet等等,它们都是servlet,只是在原有的servlet的基础上扩展了一定的功能.
从tomcat的start方法调用后,就会调到FrameworkServlet的initServletBean方法中,如下图所示:
再学一点Spring MVC_第14张图片

这里就开始真正的创建servlet上下文了.

protected WebApplicationContext initWebApplicationContext() {
  	//获取root ApplicationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
  	//判断servlet上下文是否为空,初始化的时候为空
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			//从spring容器中获取WebApplicationContext
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			//利用构造函数创建WebApplicationContext,并且设置父容器为rootApplicationContext
			wac = createWebApplicationContext(rootContext);
		}

//判断是否已经触发过上下文刷新事件
		if (!this.refreshEventReceived) {
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}
//....
//wac放到servletcontext上下文中
		return wac;
	}

DispatcherServlet初始化

servlet上下文创建成功之后,调用onRefresh()方法,此时就执行到了DispatcherServlet#init方法.

protected void initStrategies(ApplicationContext context) {
  	//1. 处理请求中的文件解析
		initMultipartResolver(context);
  	//2. 处理i18,国际化语言,默认是AcceptHeaderLocaleResolver
		initLocaleResolver(context);
  	//3. 处理主题,比如图片,样式等,默认是FixedThemeResolver
		initThemeResolver(context);
  	//4. 映射处理器,默认是BeanNameUrlHandlerMapping
		initHandlerMappings(context);
  	//5. handler处理适配器,通过它执行不同的request请求,默认是SimpleControllerHandlerAdapter
		initHandlerAdapters(context);
  	//6. handler异常解析器
		initHandlerExceptionResolvers(context);
  	//7. 视图名称解析,默认是DefaultRequestToViewNameTranslator
		initRequestToViewNameTranslator(context);
  	//8. 视图解析器,默认是InternalResourceViewResolver
		initViewResolvers(context);
  	//9.重定向设置一些信息
		initFlashMapManager(context);
	}

到这里呢,DispatchServlet就初始化完成了,servlet上下文也创建好了,webserver也启动了,这个时候,就可以接收我们正常的api请求了.

DispatcherServelt

话不多说先上图,对我们编写的controller的调用流程有个整体的印象和了解.
再学一点Spring MVC_第15张图片

调用流程:

  • 用户发送请求,请求到DispatcherServlet的doDispatch()
  • 根据URI,调用HandlerMapping获取Handler配置的对象,包含具体的Handler,拦截器链,以HandlerExecutionChain执行链对象返回
  • DispatcherServlet根据获得的Handler,遍历所有的adapter,调用supports方法,选择一个合适的adapter
  • 执行拦截器的preHandler方法,如果执行成功,继续走下面的流程,反之,就调用拦截器的afterCompletion方法
  • 提取请求中的模型数据,填充Handler入参,开始执行真正的handler方法
    a. HttpMessageConveter: 将请求消息转成对象
    b. 数据转换: string转int等等
    c. 数据格式化: 对请求的数据进行格式化,比如我们常用的时间格式
    d. 数据校验: 比如我们常用的注解,Min Max等,将结果存储到BindingResult或者Error中
  • Handler执行完毕之后,返回ModelAndView
  • 执行拦截器的postHandler方法
  • 根据视图名查询具体的view,选择合适的ViewResolver进行视图解析,调用视图解析器的render方法渲染视图
  • 调用拦截器的afterCompletion方法
  • 将结果返回给客户端给用户展示

常用组件

  • DispatcherServlet
  • 前端控制器,它是整个spring mvc流程控制中心,负责统一处理请求和响应,调用其他组件对用户请求进行处理,不需要我们做特殊的开发.
    
  • HandlerMapping
  • 处理器映射器,根据请求的url,method等信息查找对应的Handler,Handler也就是我们业务编写的controller中的每个api,HandlerMapping中就封装了对应的数据.
    
  • Handler
  •  简单称controller,对应用户不同场景下的api请求.
    
  • HandlerAdapter
  •  处理器适配器,负责调用具体的控制器controller方法,对用户发送的请求进行处理,它是对Handler进行了适配,采用了适配器模式,根据不同的请求类型,使用不同的适配器.
    
  • VewResolver
  •  视图解析器,对视图进行解析,得到对应的视图对象,常用的视图解析器有ThymeleafViewResolver,InternalResourceViewResolver等.
    
  • View
  •  视图,将Model模型数据通过页面展示给用户看.
    

请求入口

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
  	#servlet异步
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
    	//校验是否是文件表单上传
				processedRequest = checkMultipart(request);
    	//是,request转换成mutipartrequest
				multipartRequestParsed = (processedRequest != request);

				// 获取handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
      	//handler不存在,直接返回404异常
					noHandlerFound(processedRequest, response);
					return;
				}

				//获取具体的handler adapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
      	//判断是否是get/head请求
      	//判断有没有最新更新的数据,如果没有直接返回,lastModified这个字段需要前端传递的
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
      	//调用拦截器的preHandler
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// 通过反射执行具体的controller方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      	//判断servlet异步是不是已经开始了
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
    	//应用默认的视图名
				applyDefaultViewName(processedRequest, mv);
    	//调用拦截器的postHandler方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
  	//视图解析,结果渲染
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
//异常后,调用拦截器的afterCompletion
		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) {
      	//如果开启了servlet异步,调用
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
      	//如果是文件表单上传,请求上传的临时文件
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

这里的所有业务逻辑过程和上图一样,大家可自行参考.

Multipart

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        //省略
        else {
            try {
                return this.multipartResolver.resolveMultipart(request);
            }
            catch (MultipartException ex) {
                //省略
            }
        }
    }
    return request;
}
  1. 判断multipart解析器是否存在
  2. 判断request是否是multipart请求,这里它有2个实现类,一个是CommonsMultipartResolver,一个是StandardServletMultipartResolver,最终判断的标准都是content-type前缀是否是multipart/
  3. 调用resolveMultipart转换request,结果都是MultipartHttpServletRequest

HandlerExecutionChain

再学一点Spring MVC_第16张图片

在拦截器链的bean中,我们可以清晰的看到,它包含了具体要执行的handler,同时也包含执行handler的一些拦截器数组,以及执行的具体位置.

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

遍历所有的handlerMapping,查找符合条件的handlerExecutionChain.
handlerMappings就是在servlet初始化的时候设值进去的

查找所有HandlerMapping

  • Initialize the HandlerMappings used by this class.
  • If no HandlerMapping beans are defined in the BeanFactory for this namespace,
  • we default to BeanNameUrlHandlerMapping.
    private void initHandlerMappings(ApplicationContext context)

它的实现大概如下:
再学一点Spring MVC_第17张图片
下面我们详细的看下获取handler的过程.

MappingRegistry

A registry that maintains all mappings to handler methods, exposing methods
to perform lookups and providing concurrent access.
一个注册类,主要用于封装所有的handler映射关系

	class MappingRegistry {

		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

		private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

		private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

mappingLookup: HandlerMapping(RequestMappingInfo)与HandlerMethod的映射关系
urlLookup: 请求url和handlerMethod的映射关系
nameLookup: 对应requestMapping中的name
corsLookup: 跨域处理映射关系
AbstractHandlerMethodMapping继承了InitializingBean,在服务启动的时候,bean实例完成时,调用afterProperties方法,此时就会遍历spring容器中的所有bean,注册我们需要的controller数据.

public void register(T mapping, Object handler, Method method) {
			// Assert that the handler method is not a suspending one.
			if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
				Class<?>[] parameterTypes = method.getParameterTypes();
				if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
					throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
				}
			}
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

获取handler

在这里插入图片描述
这里我们主要看2种实现方式,一种是通过RequestMapping方式的,一种是通过url配置对应控制器的.
RequestMapping就对应我们平时使用的@RequestMapping这种方式
url配置的现在我们使用的都比较少了,见下图的配置方式:
再学一点Spring MVC_第18张图片下面的所有的请求方式,我们也围绕着RequestMappingHandler进行解析.
最终会调用到AbstractHandlerMethodMapping#getHandlerInternal方法,获取对应的HandlerMethod实例对象.

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
  1. 首先获取request对应路径url,具体的实现逻辑大家自行查看源码吧
  2. 设置到request域中
  3. 调用lookupHandlerMethod获取具体的HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
        //从urlLookup中获取对应的handlerMethod
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        //遍历封装Match对象,便于下面处理
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		if (!matches.isEmpty()) {
			//省略,多个配置的url情况下,选择最匹配的,如果还是大于1个,直接报错提示
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
            //没有匹配上的,返回null
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

HandlerMethod返回空,获取默认的handler,默认的也是空,就直接返回空.

封装HandlerExecutionChain

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//生成	HandlerExecutionChain对象
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
        //遍历所有的拦截器
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                //匹配请求路径,如果匹配成功,添加到拦截器链
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

HandlerAdapter

MVC framework SPI, allowing parameterization of the core MVC workflow.
Interface that must be implemented for each handler type to handle a request.
它是mvc框架的spi,每种handler类型,必须有对应的adapter实现.
如果想要按照顺讯处理,需要实现Ordered接口

public interface HandlerAdapter {

    //给定一个handler,判断当前adapter是否可以处理
	boolean supports(Object handler);

	//处理handler,调用真实的handler方法,返回ModelAndView
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;


	long getLastModified(HttpServletRequest request, Object handler);

}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	//遍历所有的handler适配器	
    if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
                //支持当前请求的,返回true
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
	}

adapter有不同的实现类,如下:
再学一点Spring MVC_第19张图片比如第一个AbstractHandlerMethodAdapter

public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}

如果handler是否HandlerMethod的实现,那么返回的就是true,也就是说当前的adapter可以处理当前请求.

RequestMappingHandlerAdapter

在对应的adapter中有相应的数据初始化,比如参数解析的处理器,参数绑定的解析器,返回值的解析器,controllerAdviceBean等等.
RequestMappingHandlerAdapter实现了InitializingBean,所以我们看下afterProperties方法即可.

public void afterPropertiesSet() {
		//初始化controller的植入功能缓存
  	//比如ModelAttribute,initBinder,responseBodyAdvice等等
		initControllerAdviceCache();
  	//参数解析
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
  	//参数绑定,数据转换
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
  	//controller返回值处理
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

有了这些就可以更方便的处理后续的真正的controller方法.

ContollerAdvice
主要用于统一异常处理,数据转换,全局数据绑定等

HandlerInterceptor#preHandler

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

执行真正controller之前的前置调用处理,可以做一些权限,日志,数据填充等等.
只要拦截器中有一个拦截器不通过,那么整个的请求就结束了.
在中间有拦截器false的时候,会调用triggerAfterCompletion方法,遍历拦截器的时候是按照正序遍历,调用triggerAfterCompletion方法的时候是按照当前已经执行到的拦截器位置,倒序遍历执行.

Handler

这里就开始调用真正我们编写的servlet方法了.

// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

  	//创建ServletWebRequest,包含request和response
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
    	//创建数据绑定工厂,主要是设置controllerAdvice中的initBinder方法,便于数据绑定和转换
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    	//创建model工厂,主要是处理controllerAdvice中的ModelAttribute属性信息
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    	//创建ServletInvocableHandlerMethod
    	//它扩展了InvocableHandlerMethod功能,实现了返回值的处理
    	//InvocableHandlerMethod实现了HandlerMethod的功能,扩展了从http请求中获取请求参数
			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,它是一个视图和数据的映射关系
    	//它的属性包含view和modelMap数据
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    	//创建异步请求request
			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    	//异步管理器
			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    	//设置异步线程池
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
    	//callable拦截器链
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
    	//defer拦截器链
			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;
			}
    	//返回ModelAndView
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

参数设置完毕之后,开始处理request请求.

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
  	//通过反射调用目标方法,并组装目标方法所需参数
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  	//设置响应状态和reason,status放置到request域中
		setResponseStatus(webRequest);

  	//如果返回值为null,说明方法已经完成了请求,设置标志位为true
		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
  	//reason不为空,表示已经处理过了,标记为true,异常的时候带有reason
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}
  	//未处理完毕,标记false
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
    	//处理返回值
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

接下来我们先看下invokeForRequest,然后看下返回值的处理流程

invokeForRequest

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
  	//参数解析
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
  	//这里就是反射调用目标方法
		return doInvoke(args);
	}
解析方法参数
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
    //获取方法参数列表
    //MethodParameter中包含参数的索引位置,参数class类型,name等
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        //根据参数类型查找对应实例的参数
        //从dispatchservlet过来的时候providedArgs是null
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        //遍历所有的参数解析器,查看看是否有support=true的解析器,不存在提示异常
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            //解析获取参数
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {
            //异常处理
        }
    }
    return args;
    }

我们先看下参数解析的基础接口HandlerMethodArgumentResolver

public interface HandlerMethodArgumentResolver {

	//针对给定的MethodParameter判断是否能够可以解析
	boolean supportsParameter(MethodParameter parameter);

	//解析参数
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

在来看下它的实现类都有哪些
再学一点Spring MVC_第20张图片
我们看一些常用的参数解析器:
● RequestParamMethodArgumentResolver
○ 对应@RequestParam参数,从http请求中根据name获取参数
● PathVariableMethodArgumentResolver
○ 对应@PathVariable参数,从url中获取参数
● RequestHeaderMethodArgumentResolver
○ 对应@RequestHeader参数,从http请求头中获取参数
● RequestResponseMethodArgumentResolver
○ 对应@RequestBody参数,提取body数据,转化为java对象
● ModelMethodProcessor
○ 参数是org.sparingframework.ui.Model及其子类
● ServletModelAttributeMethodProcessor
○ 对应@ModelAttribute参数
● ExpressionValueMethodArgumentResolver
○ 对应@Value参数,spel表达式,从spring容器中获取值
这里有非常多的参数解析器,感兴趣的同学可以点开每个实现类大致看看.

RequestResponseBodyMethodProcessor

这里我们以用的比较多的RequestResponseBodyMethodProcessor为例,简单说明一下@RquestBody是如何处理参数的.

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
        //读取http中的body消息
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		if (binderFactory != null) {
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
                //参数校验
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}

		return adaptArgumentIfNecessary(arg, parameter);
	}

这里就用到了比较重要的消息转换的组件HttpMessageConverter,它可以帮助我们把请求的请求数据转换成java对象,或者把java对象转换成输出格式的数据.

public interface HttpMessageConverter<T> {

	//标明给定的类,是否可读,对应HttpInputMessage,读取请求信息
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

	//标明给定的类,是否可写,对应HttpOutputMessage
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
	List<MediaType> getSupportedMediaTypes();

	//从输入消息中读取数据
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	//写数据
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

如果想要自定义消息转换器,只需要实现HttpMessageConverter即可.
正常我们创建一个项目后,也没有配置消息转换器,为什么http请求中的数据还是能够处理呢?是因为在服务启动的时候,会帮我们设置一些默认的消息转换器,在WebMvcConfigurationSupport中会设置一些默认的,这个类对于mvc来说是非常有用的,很多自定义的消息转换器,拦截器的添加等等都是在这里操作的,我们自定义的或者需要扩展的都是依托它.

下图就是默认的一些消息转化器
再学一点Spring MVC_第21张图片
我们也可以添加自定义的消息转化器

  1. 定义Bean对象,比如configuration中的@Bean
@Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        //1、定义一个convert转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        //省略
        return new HttpMessageConverters(converter);
    }
  1. 扩展support的configureMessageConverters
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // add方法可以指定顺序,有多个自定义的WebMvcConfigurerAdapter时,可以改变相互之间的顺序
        // 但是都在springmvc内置的converter前面
        converters.add(new xxx());
    }
  1. 扩展support的extendMessageConverters
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new JavaSerializationConverter());
    }

接下来看下真正读取消息的业务逻辑,看下主要的readMessage的业务

//遍历所有的消息转化器
for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
        			//判断是否可读取
    				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
    					//判断http请求中是否有body消息
                        if (message.hasBody()) {
                        //调用request advice before
						HttpInputMessage msgToUse =
								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						//通过反序列化获取数据
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
						//调用request advice after
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
					}
					else {
						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
					}
					break;
				}
			}

读取消息的时候我们看到了有个advice的处理,这个就是RquestBodyAdvice,可以在处理HttpInputMessage前后对消息进行额外的处理.比如这个demo,实现RquestBodyAdvice即可.
再学一点Spring MVC_第22张图片
顺便看下HttpOutputMessage的处理逻辑

for (HttpMessageConverter<?> converter : this.messageConverters) {
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
						(GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}
					return;
				}
			}

写的逻辑和读的逻辑都差不多了,同时在写之前也看到了一个getAdvice().beforeBodyWrite()方法,很多的业务逻辑中,都要求所有的输出必须要按照统一的格式进行处理,这个时候输出的advice就派上用场了.
这个和上面定义的RequestBodyAdvice相对应的它就是ResponseBodyAdvice,然后我们自定义实现它即可.

handleReturnValue

HandlerMethodReturnValueHandler是一个处理handler返回值的处理器

public interface HandlerMethodReturnValueHandler {

	//判断是否可以处理这个返回值参数
	boolean supportsReturnType(MethodParameter returnType);
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

先看下处理返回值的业务流程

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    	//根据返回值类型和值从HandlerMethodReturnValueHandler集合中获取support=true的
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
    	//处理返回值
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

HandlerMethodReturnValueHandler的实现大致有下面的这些,我们也可以自定义返回值处理器,只需要实现HandlerMethodReturnValueHandler就行了.
再学一点Spring MVC_第23张图片

  1. ViewNameMethodReturnValueHandler
    a. 返回值为视图名称时的解析器
  2. MapMethodProcessor
    a. 返回值为 Map 的解析器
  3. StreamingResponseBodyReturnValueHandler
    a. 返回值为 ResponseEntity 类型时的解析器
  4. DeferredResultMethodReturnValueHandler
    a. 返回值为 DeferredResult 类型时的解析器,表示异步请求
  5. CallableMethodReturnValueHandler
    a. 返回值为 Callable 类型时的解析器,表示异步请求
  6. ModelMethodProcessor
    a. 返回值为 Model 类型时的解析器
  7. ModelAndViewMethodReturnValueHandler
    a. 返回值为 ModelAndView 类型时的解析器
  8. RequestResponseBodyMethodProcessor
    a. 方法上标注有@ResponseBody 注解时返回值的解析器
  9. HttpEntityMethodProcessor
    a. 返回值为 HttpEntity 类型但是非 RequestEntity 类型时的解析器
    这里我们还是以RequestResponseBodyMethodProcessor为例
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    	//设置返回值已处理标识
		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		//写数据到HttpOutMessage中
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

applyPostHandle拦截器后置处理

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

遍历所有拦截器,倒序执行postHandler方法

视图渲染

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

		boolean errorView = false;

		if (exception != null) {
            //ModelAndViewDefining异常
			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方法,渲染视图
			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);
		}
	}

ExceptionHandler

在异常处理的过程中,都会执行到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException方法.

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

		ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);

根据异常和HandlerMethod查找对应异常处理器,在getExceptionHandlerMethod方法中,就有我们常用的全局异常处理.

//遍历所有的advice中定义的ExceptionHandler,判断哪个可以处理当前Exception
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
			ControllerAdviceBean advice = entry.getKey();
			if (advice.isApplicableToBeanType(handlerType)) {
				ExceptionHandlerMethodResolver resolver = entry.getValue();
				Method method = resolver.resolveMethod(exception);
				if (method != null) {
					return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
				}
			}
		}

渲染render

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		Locale locale =
				(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

        //使用视图解析器得到视图view对象
		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
            //遍历所有的视图解析器
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		}
		else {
			view = mv.getView();
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
            //调用视图的render方法渲染视图,将结果输出到客户端
			view.render(mv.getModelInternal(), request, response);
		}
	}

调用view视图的AbstractView#render方法输出model数据

public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
    	//合成输出数据,包含静态和动态数据
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response);
        //设置数据到requestAttribute中,跳转指定视图页面
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}
freemarker demo

这里我们使用freemarker做个简单的demo
pom引入对应jar

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

properties添加配置

spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.allow-request-override=false
spring.freemarker.check-template-location=true

#类型
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true

#文件后缀
spring.freemarker.suffix=.ftl
#路径
spring.freemarker.template-loader-path=classpath:/templates

编写我们的controller

@RestController
public class Controller {

    @RequestMapping("freemarker1")
    public ModelAndView freemarker() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("username", "test");
        mv.setViewName("freemarker");
        return mv;
    }
}

编写简单的页面代码

<html>
<head>
    <title>FreeMarker</title>
</head>
<body>
<b>Welcome!</b>
<i>${username }</i>
</body>
</html>

当我们访问localhost:8080/freemarker1的时候就会出现我们指定的页面数据了.

结语

整个springmvc的流程还是比较清晰的,但是还是需要各位多多调试,多看源码才能深刻理解其中含义.

你可能感兴趣的:(spring,mvc,spring,mvc,java)