spring 源码解读-MVC
一、MVC请求处理流程
- DispatcherServlet是SpringMVC中的前端控制器,负责接收request ,并将request转发给对应组件。
- HanderMapping是完成url到Controller的映射组件,DispatcherServlet接收request ,然后前端控制器请求处理器映射器。(HandlerMapping)去查找处理器(Handler),找到以后处理器映射器(HandlerMappering)向前端控制器返回执行链(HandlerExecutionChain)。
- 前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)。
- 处理器适配器去执行Handler,处理器执行完向前端控制器返回ModelAndView。
- 前端控制器请求视图解析器(ViewResolver)去进行视图解析。然后视图解析器向前端控制器返回View。
- 前端控制器对视图进行渲染并向用户响应结果。
二、MVC九大组件
- HanderMapping:处理器映射器,用来查找Hander,具体的表现形式可以是类也可以是方法,HanderMapping在请求到达后,它的作用就是找到请求相应的处理器Hander和Interceptors。
- HandlerAdapter:处理器适配器,SpringMVC中的Hander可以是任意形式,只要能处理请求就行,由于Servlet的方法结构都是如
doService(HttpServletRequest req,HttpServletResponse rsp)
这样的形式,让固定的Servlet处理方法调用Hander来进行处理,这一步就要HandlerAdapter来执行。 - HandlerExceptionResolvers:这个是来处理Hander过程中出现的异常情况,它的作用就是根据异常来设置ModelAndView页面,它只负责请求处理阶段的异常,而渲染阶段的异常则不属于它。
- ViewResolver:视图解析器,它的作用是将String类型的视图和Locale解析为View类型的视图,Controller返回的String类型的视图名称ViewName,最终会被解析成View(渲染页面)
- RequestToViewNameTranslator:作用就是从Request中获取ViewName,因为ViewResolver是根据ViewName查找View的,但是有的Hander处理完成之后,没有设置View也没有ViewName,这个时候就需要这个组件从Request中获取ViewName
- LocaleResolver:ViewResolver的resolveViewResolvers()方法,需要两个参数,第二个参数 Locale的产生,就是LocaleResolver的作用,LocaleResolver从request中解析出Locale,中国大陆这个值就是zh-CN表示一个区域,这个也是i18n国际化的基础。
- ThemeResolver:主题解析,主题就是样式css、图片以及它们形成的显示效果集合,一套主题对应一个porperties文件,里面存放和主题相关的所有资源。ThemeResolver从request中解析出主题名,找到对应的具体主题。
- MultipartResolver:用于处理上传请求,将普通的request包装成MultipartHttpServletRequest 来实现,HttpServletRequest 可以通过getFile() 直接获取文件,如果是多文件上传,还可以通过调用getFileMap()得到 Map(FileName,File) 这样的结构。
- FlashMapManager:FlashMap是用于重定向 Redirect时参数数据传递,因为Redirect没有参数传递的功能,只能通过FlashMap来传递,在Redirect之前将参数写入request。FlashMapManager就是用来管理FlashMap的。
三、源码分析
主要分三个部分:
- 一、ApplicationContext初始化时,用Map保存所有的url和Controller类的对应关系;
- 二、根据url找到对应的Controller,并从Controller中找到处理请求的方法;
- 三、request的参数绑定到方法形参,执行方法处理,并返回结果视图。
1、初始化
DispatcherServlet 里的init()方法,然后发现其init()方法是在父类HttpServletBean中,源码如下:
public final void init() throws ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
}
try {
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
throw var4;
}
this.initServletBean();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
}
}
继续跟踪 this.initServletBean();进入:
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
} catch (RuntimeException var6) {
this.logger.error("Context initialization failed", var6);
throw var6;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
}
}
这里主要逻辑就是初始化IOC容器,最终会调用 refresh()方法,前面IOC容器的初始化讲过,IOC容器初始化之后最终调用onRefresh()方法,最终是在DispatcherServlet中实现,源码如下:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化多文件上传组件
this.initMultipartResolver(context);
//初始化本地语言环境
this.initLocaleResolver(context);
//初始化主题模板解析器
this.initThemeResolver(context);
//initHandlerMappings初始化处理器映射器
this.initHandlerMappings(context);
//初始化处理器适配器
this.initHandlerAdapters(context);
//初始化异常拦截器
this.initHandlerExceptionResolvers(context);
//初始化视图预处理器
this.initRequestToViewNameTranslator(context);
//初始化视图处理器
this.initViewResolvers(context);
//初始化FlashMap管理器
this.initFlashMapManager(context);
}
到这一步初始化动作完成,下一步主要看initHandlerMappings,映射关系的建立,主要是HandlerMapping接口的子类AbstractDetectingUrlHandlerMapping类的initApplicationContext方法,源码如下:
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
this.detectHandlers();
}
protected void detectHandlers() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking for URL mappings in application context: " + this.getApplicationContext());
}
String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class);
String[] var2 = beanNames;
int var3 = beanNames.length;
for(int var4 = 0; var4 < var3; ++var4) {
String beanName = var2[var4];
String[] urls = this.determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
this.registerHandler(urls, beanName);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
determineUrlsForHandler 方法作用就是获取每个Controller中url,不同的子类有不同的实现方式,这个就是典型的模板方法模式,BeanNameUrlHandlerMapping就是AbstractDetectingUrlHandlerMapping类的子类,处理注解形式的url映射,目前都是注解开发,所以BeanNameUrlHandlerMapping是如何查BeanName上映射的url?
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = this.getApplicationContext().getAliases(beanName);
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
这里就完成了HanderMapping映射关系。
2、运行
DispatcherServlet 的核心方法doService()方法,该方法中的核心逻辑是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;
Object dispatchException = null;
try {
//检查是否文件上传请求
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//获取当前的Control,Handler处理器,
//并不是直接返回Handler,而且返回处理器链对象,封装了Handler和intercept
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//获取处理request的处理器适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//实际的处理器处理请求,返回结果视图对象MV
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//结果视图对象的处理
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
//请求成功响应之后的方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
具体调用RequestMappingHandlerAdapter的handle()方法,核心逻辑handleInternal()方法
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
this.checkRequest(request);
ModelAndView mav;
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader("Cache-Control")) {
if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
this.prepareResponse(response);
}
}
return mav;
}
整个处理过程最核心的逻辑就是拼接controller的url和方法的url与request的url进行匹配,找到匹配的方法。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndView var15;
try {
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
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);
Object result;
if (asyncManager.hasConcurrentResult()) {
result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
if (asyncManager.isConcurrentHandlingStarted()) {
result = null;
return (ModelAndView)result;
}
var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
webRequest.requestCompleted();
}
return var15;
}
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]); 最终要实现的目的就是:完成request中的参数和方法参数上的数据绑定。