DispatchServlet视图解析过程分析
image.png以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);
// 上面都是一些准备工作,重点看doService方法;doService是一个抽象方法,由子类实现并处理request请求
try {
doService(request, response);
}
...
// 省略了catch和finally相关代码
}
下面分析FrameworkServlet的实现类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);
// 上面设置request的一些属性,重点看doDispatch方法
try {
doDispatch(request, response);
}
...
// 省略了catch和finally相关代码
}
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) {
// 没有找到对应的处理器,返回404状态
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;
}
}
// 请求处理之前,调用拦截器的preHandle方法预处理,一般是权限验证,如果返回false,结束请求
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用AnnotationMethodHandlerAdapter的handler方法处理request请求,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 请求处理之后渲染页面之前,调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 处理handler返回的结果,即渲染页面
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;
}
// 调用拦截器的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);
// 根据视图名称解析成视图对象,例如:视图解析器FreeMarkerViewResolver将视图名解析成FreeMarkerView并完成FreeMarkerView的初始化操作,设置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 {
// 视图对象执行渲染操作
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方法进行渲染,这是一个抽象方法,由子类实现
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的一个子类,实现了renderMergedOutputModel方法
org.springframework.web.servlet.view.AbstractTemplateView:
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// 将RequestContext添加到model中,默认添加
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));
}
// 设置response的contentType
applyContentType(response);
// 抽象方法,由子类实现
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 {
// 可以在这里执行一些帮助操作,FreeMarkerView中此方法为空
exposeHelpers(model, request);
// FreeMarkerView执行渲染
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模板处理结果响应到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创建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 {
// 自动导入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);
}
网友评论