MVC模式,全称是Model-View-Controller(模型-视图-控制器)模式,它是一种软件架构模式,目标是将软件的用户界面和业务逻辑隔离,使代码具有更高的可扩展性,可复用行,可维护性以及灵活性.
MVC模式将应用程序划分成模型、视图、控制器等三层,如下图所示:
概念解释:
它是应用程序的实体部分,或者说是需要展示的数据,比如我们定义的bean,dto,vo等,也包含业务处理的bean,比如service,dao等等.
它是指应用程序中与浏览器进行交互,展示数据的资源,web应用中我们可以认为是前台页面,比如jsp,html等等.
它是指我们编写的servelt,负责将用户的请求交给模型层进行处理,将model返回,用视图view渲染并展示给用户,controller层不做任何业务处理,只是view和model的中间桥梁.
MVC模式的优点:
完整的DispatcherServlet初始化流程,详见下图:
这里我们以springboot为例进行springmvc初始化流程的分析,大家需要注意了,如果是不使用springboot的项目,前面的流程不一样,大家可以自行去百度搜索,spring项目主要是通过ContextLoaderListener中处理的.
为什么在springboot项目,我们没有配置tomcat,但是也可以正常的服务启动,这是因为它里面内嵌了tomcat.
在AbstarctApplicationContext#onRefresh方法中的onRefresh方法中就会创建webServer
webServer通过ServletWebServerFacotry获得.
看到这个方法,大家是不是觉得很熟悉,我们在tomcat的xml文件中,也可以看到baseDir,engine,connector等等,这里不是我们说的重点.
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实现这个功能.
LifecycleProcessor继承了Lifecycle接口,同时提供了onRefresh和onClose的方法,当ApplicationContext上下文刷新或者关闭的时候,会触发上面的两个方法.
SmartLifecycle继承了Lifecycle接口,同时继承了Phased接口.
容器中实现了Lifycycle的多个类如果希望有序的进行回调时,那么启动和关闭调用的顺序很重要,如果两个对象之间存在依赖关系,那么依赖方必须在依赖后执行,这里就是上面定义的Phased接口.
getPhase()返回值最小的对象优先执行,值越小,优先级越高,但是调用stop的时候正好相反,值越大,优先级越高.
ApplicationContext在onRefresh调用的时候,创建时DefaultLifecycleProcessor对象.
调用start方法就会执行到webServer的生命周期bean的start方法中
到了这里就是真正的tomcat启动的流程了,容器启动后就会创建web容器上下文,并且初始化DispatchServlet.
tomcat启动完毕之后,发布容器初始化完成事件.
DispatchServlet,FrameworkServlet等等,它们都是servlet,只是在原有的servlet的基础上扩展了一定的功能.
从tomcat的start方法调用后,就会调到FrameworkServlet的initServletBean方法中,如下图所示:
这里就开始真正的创建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;
}
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请求了.
话不多说先上图,对我们编写的controller的调用流程有个整体的印象和了解.
调用流程:
前端控制器,它是整个spring mvc流程控制中心,负责统一处理请求和响应,调用其他组件对用户请求进行处理,不需要我们做特殊的开发.
处理器映射器,根据请求的url,method等信息查找对应的Handler,Handler也就是我们业务编写的controller中的每个api,HandlerMapping中就封装了对应的数据.
简单称controller,对应用户不同场景下的api请求.
处理器适配器,负责调用具体的控制器controller方法,对用户发送的请求进行处理,它是对Handler进行了适配,采用了适配器模式,根据不同的请求类型,使用不同的适配器.
视图解析器,对视图进行解析,得到对应的视图对象,常用的视图解析器有ThymeleafViewResolver,InternalResourceViewResolver等.
视图,将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);
}
}
}
}
这里的所有业务逻辑过程和上图一样,大家可自行参考.
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;
}
在拦截器链的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
它的实现大概如下:
下面我们详细的看下获取handler的过程.
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();
}
}
这里我们主要看2种实现方式,一种是通过RequestMapping方式的,一种是通过url配置对应控制器的.
RequestMapping就对应我们平时使用的@RequestMapping这种方式
url配置的现在我们使用的都比较少了,见下图的配置方式:
下面的所有的请求方式,我们也围绕着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();
}
}
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,默认的也是空,就直接返回空.
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;
}
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有不同的实现类,如下:
比如第一个AbstractHandlerMethodAdapter
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
如果handler是否HandlerMethod的实现,那么返回的就是true,也就是说当前的adapter可以处理当前请求.
在对应的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
主要用于统一异常处理,数据转换,全局数据绑定等
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方法的时候是按照当前已经执行到的拦截器位置,倒序遍历执行.
这里就开始调用真正我们编写的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,然后看下返回值的处理流程
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;
}
在来看下它的实现类都有哪些
我们看一些常用的参数解析器:
● 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为例,简单说明一下@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来说是非常有用的,很多自定义的消息转换器,拦截器的添加等等都是在这里操作的,我们自定义的或者需要扩展的都是依托它.
下图就是默认的一些消息转化器
我们也可以添加自定义的消息转化器
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
//1、定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//省略
return new HttpMessageConverters(converter);
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// add方法可以指定顺序,有多个自定义的WebMvcConfigurerAdapter时,可以改变相互之间的顺序
// 但是都在springmvc内置的converter前面
converters.add(new xxx());
}
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即可.
顺便看下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,然后我们自定义实现它即可.
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就行了.
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);
}
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);
}
}
在异常处理的过程中,都会执行到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);
}
}
}
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
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的流程还是比较清晰的,但是还是需要各位多多调试,多看源码才能深刻理解其中含义.