Spring MVC的大致流程:
- 在Spring MVC框架下,请求通常会被拦截交给DispatcherServlet进行处理,DispatcherServlet会根据配置信息(HandlerMapping)调用合适的handler进行处理;
- handler会进行相关业务逻辑处理,必要的时候会通过dao层与数据库进行数据交互;
处理完成之后会将处理结果封装到一个Model中,这个Model通常包含两部分信息:视图名信息以及处理结果数据信息; - DispatcherServlet会根据handler返回的Model调用对应的视图;
- 视图负责将Model中的数据信息以合适的方式展现给用户。
Spring MVC的流程基本上就如上面所述,本次主要对3,4步进行介绍
1 初始化视图解析器
在启动web容器时,会根据web.xml配置文件,在指定时间启动Spring MVC,此时会进行相关初始化工作:
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
注意语句this.initViewResolvers(context);
语句,Spring MVC会根据配置文件初始化对应视图解析器,并所有的视图解析器实例对象放入到一个List集合中,用于后面的视图解析;
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
} else {
try {
ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
} catch (NoSuchBeanDefinitionException var3) {
;
}
}
2 DispatcherServlet转发请求获得处理结果
这里没有详细说,实际上是请求被拦截,交给DispatcherServlet,DispatcherServlet通过doDispatcher()进行转发,并获得处理结果ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); //对ModelAndView进行进一步处理
...
this.render(mv, request, response); //调用该方法来处理mv
3 获取视图对象
先看看render()方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
/**省略代码**/
View view;
/**省略代码**/ //1
view = this.resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
/**省略代码**/ //2
view.render(mv.getModelInternal(), request, response);
}
3.1 获取视图对象
通过代码1 获取视图对象,具体就是通过视图解析器对ModelAndView进行解析,将逻辑视图映射到实际视图上:
//该方法,会调用视图解析器的List集合,从中获取能够解析mv的视图解析器
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
Iterator var5 = this.viewResolvers.iterator();
View view;
do {
if (!var5.hasNext()) {
return null;
}
ViewResolver viewResolver = (ViewResolver)var5.next();
//对视图进行解析,并返回视图对象
view = viewResolver.resolveViewName(viewName, locale);
} while(view == null);
return view;
}
接下来看看试图解析器是如何对mv进行解析的:
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!this.isCache()) {
return this.createView(viewName, locale);
} else {
Object cacheKey = this.getCacheKey(viewName, locale);
View view = (View)this.viewAccessCache.get(cacheKey);
if (view == null) {
Map var5 = this.viewCreationCache;
synchronized(this.viewCreationCache) {
view = (View)this.viewCreationCache.get(cacheKey);
if (view == null) {
//如果缓存Map集合中没有对应的视图对象的时候,才进行mv的解析
view = this.createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return view != UNRESOLVED_VIEW ? view : null;
}
}
上面这个方法需要重点说明一下:该方法属于AbstractCachingViewResolver类,在该类中定义了这样一个成员变量:
private final Map<Object, View> viewAccessCache = new ConcurrentHashMap(1024);
该线程盖全集合用于缓存已经解析过的视图对象,以视图名(逻辑视图名)和视图对象的key-value形式存放,一遍复用。
如果缓存Map集合中没有对应的视图对象的时候,才进行mv的解析,并将解析的视图对象存放在缓存集合中
再看具体的解析过程:
//viewNmae是逻辑视图名,经过太多太多代码了,容易忘记
protected View createView(String viewName, Locale locale) throws Exception {
if (!this.canHandle(viewName, locale)) {
return null;
} else {
String forwardUrl;
//对应集中转发情形
if (viewName.startsWith("redirect:")) {
forwardUrl = viewName.substring("redirect:".length());
RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());
view.setHosts(this.getRedirectHosts());
return this.applyLifecycleMethods(viewName, view);
} else if (viewName.startsWith("forward:")) {
forwardUrl = viewName.substring("forward:".length());
return new InternalResourceView(forwardUrl);
} else {
//我们是最普通的方式,调用该方法
return super.createView(viewName, locale);
}
}
}
然后会依次代用createView() –> loadView() –>buildView()
最终会调用UrlBasedViewResolver类中的buildView方法,在该方法中会根据配置文件中定义的prefix和suffix,设置该View对象的url
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(this.getViewClass());
//设置该View对象的url
view.setUrl(this.getPrefix() + viewName + this.getSuffix());
/**省略代码**/
//最终返回我们需要的view对象,该对象中包含实际视图的位置:url
return view;
}
3.2 得到实际视图
看第三章开头的代码,执行完代码1之后会接着执行代码2,实现具体的视图映射功能。通过获得的view对象调用render()方法,该方法又会滴啊用renderMergedOutputModel()方法:
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//request就是本次请求信息,,把mv中的数据model封装在request对象里,setAttribute()方法设定
this.exposeModelAsRequestAttributes(model, request);
this.exposeHelpers(request);
//实际视图名
String dispatcherPath = this.prepareForRendering(request, response);
//Request中的forward()方法
RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
rd.forward(request, response);
/**
*也有可能是rd.include()方法;
**/
}
最终是借助于Request中的forward方法找到实际视图,转发给用户。