DispatchServlet視圖解析過程分析
以GET方法說明
org.springframework.web.servlet.FrameworkServlet:
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
// 上面都是一些準(zhǔn)備工作,重點看doService方法;doService是一個抽象方法,由子類實現(xiàn)并處理request請求
try {
doService(request, response);
}
...
// 省略了catch和finally相關(guān)代碼
}
下面分析FrameworkServlet的實現(xiàn)類DispatcherServlet的doService方法
org.springframework.web.servlet.DispatcherServlet:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
// 上面設(shè)置request的一些屬性秒裕,重點看doDispatch方法
try {
doDispatch(request, response);
}
...
// 省略了catch和finally相關(guān)代碼
}
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;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 從handlerMappings找到處理request的處理器handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// 沒有找到對應(yīng)的處理器母市,返回404狀態(tài)
noHandlerFound(processedRequest, response);
return;
}
// 從handlerAdapters找到支持handler的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 請求處理之前垢箕,調(diào)用攔截器的preHandle方法預(yù)處理充边,一般是權(quán)限驗證,如果返回false蒜茴,結(jié)束請求
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 調(diào)用AnnotationMethodHandlerAdapter的handler方法處理request請求,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 請求處理之后渲染頁面之前浆西,調(diào)用攔截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 處理handler返回的結(jié)果粉私,即渲染頁面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
...
// 省略catch和finally代碼塊
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
// 處理異常情況,自定義的異常處理類可以參考DefaultHandlerExceptionResolver
if (exception != null) {
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);
}
}
// 渲染頁面近零,通過視圖名稱進行視圖解析
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// 調(diào)用攔截器的afterCompletion毡鉴,一般是處理資源回收操作
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
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);
// 根據(jù)視圖名稱解析成視圖對象崔泵,例如:視圖解析器FreeMarkerViewResolver將視圖名解析成FreeMarkerView并完成FreeMarkerView的初始化操作,設(shè)置FreeMarkerConfig
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
// 視圖對象執(zhí)行渲染操作
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
下面解析視圖渲染過程猪瞬,實際是renderMergedOutputModel方法進行渲染憎瘸,這是一個抽象方法,由子類實現(xiàn)
org.springframework.web.servlet.view.AbstractView:
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);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
protected abstract void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
AbstractTemplateView是AbstractView的一個子類陈瘦,實現(xiàn)了renderMergedOutputModel方法
org.springframework.web.servlet.view.AbstractTemplateView:
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// 將RequestContext添加到model中幌甘,默認(rèn)添加
if (this.exposeSpringMacroHelpers) {
if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) {
throw new ServletException(
"Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE +
"' because of an existing model object of the same name");
}
// Expose RequestContext instance for Spring macros.
model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, response, getServletContext(), model));
}
// 設(shè)置response的contentType
applyContentType(response);
// 抽象方法,由子類實現(xiàn)
renderMergedTemplateModel(model, request, response);
}
protected abstract void renderMergedTemplateModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
重點來了痊项,這里引出FreeMarkerView锅风,它是AbstractTemplateView的一個子類
org.springframework.web.servlet.view.freemarker.FreeMarkerView:
protected void renderMergedTemplateModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 可以在這里執(zhí)行一些幫助操作,F(xiàn)reeMarkerView中此方法為空
exposeHelpers(model, request);
// FreeMarkerView執(zhí)行渲染
doRender(model, request, response);
}
protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 這里遍歷model中的key-value鞍泉,添加到request的屬性中
exposeModelAsRequestAttributes(model, request);
// Expose all standard FreeMarker hash models.
SimpleHash fmModel = buildTemplateModel(model, request, response);
if (logger.isDebugEnabled()) {
logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale(request);
// 將FreeMarker模板處理結(jié)果響應(yīng)到response
processTemplate(getTemplate(locale), fmModel, response);
}
protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response)
throws IOException, TemplateException {
template.process(model, response.getWriter());
}
接下來就看到FreeMarker核心的API了皱埠,通過Configuration創(chuàng)建template
Configuration cfg = new Configuration();
...
Template myTemplate = cfg.getTemplate("myTemplate.html");
freemarker.template.Template:
public void process(Object rootMap, Writer out)
throws TemplateException, IOException
{
createProcessingEnvironment(rootMap, out, null).process();
}
freemarker.core.Environment
public void process() throws TemplateException, IOException {
Object savedEnv = threadEnv.get();
threadEnv.set(this);
try {
// Cached values from a previous execution are possibly outdated.
clearCachedValues();
try {
// 自動導(dǎo)入freemarker.properties中的auto_import和auto_include
doAutoImportsAndIncludes(this);
visit(getTemplate().getRootTreeNode());
// It's here as we must not flush if there was an exception.
if (getAutoFlush()) {
out.flush();
}
} finally {
// It's just to allow the GC to free memory...
clearCachedValues();
}
} finally {
threadEnv.set(savedEnv);
}
}
附錄
一個FreeMarker解析ftl模板的示例,其中咖驮,hello.ftl是ftl模板边器,hello.html是最終解析成html的文件
Configuration configuration = new Configuration();
File outFile = null;
try {
configuration.setDirectoryForTemplateLoading(new File("/WEB-INF/ftl/template"));
Template template = configuration.getTemplate("hello.ftl");
outFile = new File("hello.html");
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream, "UTF-8");
MapModel mapModel = new MapModel(dataMap, new BeansWrapper());
template.process(mapModel, writer);
writer.close();
fileOutputStream.close();
} catch (Exception e) {
LOGGER.info(e.getMessage(), e);
}