SpringMVC源码情操陶冶-View视图渲染
本节简单分析View视图对象的render方法
View接口
最重要的就是render()方法,具体源码如下
/**
* Render the view given the specified model.
* <p>The first step will be preparing the request: In the JSP case,
* this would mean setting model objects as request attributes.
* The second step will be the actual rendering of the view,
* for example including the JSP via a RequestDispatcher.
* @param model Map with name Strings as keys and corresponding model
* objects as values (Map can also be {@code null} in case of empty model)
* @param request current HTTP request
* @param response HTTP response we are building
* @throws Exception if rendering failed
*/
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
下面我们就此接口方法展开分析
AbstractView
直接看源码
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//综合model、内部属性staticAttributes和request对象中的View.PATH_VARIABLES,都封装在同一个Map集合中
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//为response做准备,默认是针对download请求
prepareResponse(request, response);
//真实处理render操作,供子类实现调用
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
AbstractTemplateView-AbstractView的子类
抽象模板视图,源码如下
@Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//判断是否暴露request属性给前端,是则将所有的request属性加到model中
if (this.exposeRequestAttributes) {
for (Enumeration<String> en = request.getAttributeNames(); en.hasMoreElements();) {
String attribute = en.nextElement();
//当不允许request对象中的属性被覆盖且model存在相同key时,会抛异常
if (model.containsKey(attribute) && !this.allowRequestOverride) {
throw new ServletException("Cannot expose request attribute '" + attribute +
"' because of an existing model object of the same name");
}
//允许则直接通过
Object attributeValue = request.getAttribute(attribute);
model.put(attribute, attributeValue);
}
}
//等同request
if (this.exposeSessionAttributes) {
HttpSession session = request.getSession(false);
if (session != null) {
for (Enumeration<String> en = session.getAttributeNames(); en.hasMoreElements();) {
String attribute = en.nextElement();
if (model.containsKey(attribute) && !this.allowSessionOverride) {
throw new ServletException("Cannot expose session attribute '" + attribute +
"' because of an existing model object of the same name");
}
Object attributeValue = session.getAttribute(attribute);
if (logger.isDebugEnabled()) {
logger.debug("Exposing session attribute '" + attribute +
"' with value [" + attributeValue + "] to model");
}
model.put(attribute, attributeValue);
}
}
}
//设置SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE属性到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));
}
//设置返回给前端的内容类型,可在ViewResolver中设置contentType属性
applyContentType(response);
//抽象方法供子类调用实现
renderMergedTemplateModel(model, request, response);
}
由以上代码分析可得,此类主要是判断是否将request和session的对象中的所有属性添加到Map类型的model对象中
判断条件为:
- exposeRequestAttributes设置为true,表明将request中的Attributes属性添加到model中,这样前端可以直接引用request对象中的attribute属性。其默认为false
- exposeSessionAttributes设置为true,表明将session中的Attributes属性添加到model中,这样前端可以直接引用session对象中的attribute属性。其默认为false
- allowRequestOverride/allowSessionOverride设置为true,表明是否允许request/session对象中的attribute属性覆盖model中的同key键。其默认为false,在出现上述情况则会抛出异常
FreeMakerView-AbstractTemplateView实现类
关于AbstractTemplateView的实现类有很多,本文则选取常用的FreemarkerView
来进行简析
FreeMarkerView#initServletContext()-初始化方法
此方法在父类WebApplicationObjectSupport#initApplicationContext()
中被调用
protected void initServletContext(ServletContext servletContext) throws BeansException {
if (getConfiguration() != null) {
this.taglibFactory = new TaglibFactory(servletContext);
}
else {
//查询springmvc上下文中是否存在FreeMarkerConfig接口bean,其一般是通过FreeMarkerConfigurer来注册的
//如果不存在FreeMarkerConfig bean对象则会抛异常
FreeMarkerConfig config = autodetectConfiguration();
setConfiguration(config.getConfiguration());
this.taglibFactory = config.getTaglibFactory();
}
//初始servlet对象,其实是创建ServletContextResourceLoader对象,帮助可以获取"/WEB-INF/"下的资源
GenericServlet servlet = new GenericServletAdapter();
try {
servlet.init(new DelegatingServletConfig());
}
catch (ServletException ex) {
throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex);
}
this.servletContextHashModel = new ServletContextHashModel(servlet, getObjectWrapper());
}
此处主要获取FreeMarkerConfig对象,其一般由FreeMarkerConfigurer对象生成,可指定加载资源的路径和设置输出的一些属性,且这是必须注册到springmvc的bean工厂的
FreeMarkerView#renderMergedTemplateModel()-渲染视图
源码奉上
protected void renderMergedTemplateModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//默认为空,待实现
exposeHelpers(model, request);
//启动渲染程序
doRender(model, request, response);
}
FreeMarkerView#doRender()-渲染操作
源码奉上
protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 这里其实是将model中的所有属性都注册到request对象中
exposeModelAsRequestAttributes(model, request);
// Expose all standard FreeMarker hash models.将FreeMarker的属性放到一个Map中
SimpleHash fmModel = buildTemplateModel(model, request, response);
//此处的getUrl()由ViewResolver来设定,为prefix+viewName+suffix
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);
//此处的操作比较复杂,需要慢慢分析
processTemplate(getTemplate(locale), fmModel, response);
}
上述的代码具体解析分为两部分:模板对象Template获取和Template对象的处理方法process(),下面将从这两部分进行简单的展开
1.FreeMarkerView#getTemplate()
获取模板对象
protected Template getTemplate(Locale locale) throws IOException {
//templateName为prefix+viewName+suffix
return getTemplate(getUrl(), locale);
}
protected Template getTemplate(String name, Locale locale) throws IOException {
//最终是通过FreeMarkerConfigurer的内部属性Configuration来获取模板对象
return (getEncoding() != null ?
getConfiguration().getTemplate(name, locale, getEncoding()) :
getConfiguration().getTemplate(name, locale));
}
此处限于FreeMaker相关类Configuration
获取Template模板对象的步骤过于冗长,这里作下总结,具体读者可自行去阅读源码分析
获取Template对象是由
TemplateCache
来实现的,其是通过prefix+viewName+suffix
作为整个文件名去找寻,其中prefix参数值不能以/
为开头,这在FreeMarker/Velocity是约定的,而jsp页面引擎InternalView支持/
开头的prefix,这点需要区别开来具体的获取资源文件是通过Configuration指定的templateLoader去加载实际的资源,一般此处的templateLoader为
SpringTemplateLoader
,支持templateLoaderPath为classpath:
搜索
2.FreeMarker#process()
此处主要是通过获取到的Template对象,对其持有的实际资源进行读取渲染后再重新写入以完成
${}
这样字符的含义model中所有的数据都会保留到request的Attributes对象中,所以FreeMarker可直接通过${attribute}获取相应的参数值
小结
ViewResolver帮助我们设置好对应的视图解析器比如FreeMarkerView,包含加载资源的prefix/suffix,以及是否将request/session对象属性绑定到model对象中
本文则以FreeMarkerView如何解析资源入手,简单的分析其中的操作逻辑,具体的细节读者可自行查询
SpringMVC源码情操陶冶-View视图渲染的更多相关文章
- SpringMVC源码情操陶冶-ViewResolver视图解析
简单分析springmvc是如何解析view视图,并返回页面给前端 SpringMVC配置视图解析器 <bean id="viewResolver" class=" ...
- SpringMVC源码情操陶冶-FreeMarker之web配置
前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...
- SpringMVC源码情操陶冶-DispatcherServlet类简析(一)
阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...
- SpringMVC源码情操陶冶-HandlerAdapter适配器简析
springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...
- SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器
承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...
- SpringMVC源码情操陶冶-DispatcherServlet
本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...
- SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...
- SpringMVC源码情操陶冶-DispatcherServlet父类简析
阅读源码有助于陶冶情操,本文对springmvc作个简单的向导 springmvc-web.xml配置 <servlet> <servlet-name>dispatch< ...
随机推荐
- 在.net下打造mongoDb基于官方驱动最新版本
还是一如既往先把结构图放出来,上上个版本添加了redis的缓存,但是不满足我的需求,因为公司有项目要求是分布式所以呢,这里我就增加了mongoDb进行缓存分布式,好了先看结构图. 总的来说比较蛋疼,因 ...
- 浅谈Android studio中OKHttp安装及简单使用
Google貌似在6.0版本里面删除了HttpClient相关API,鉴于okhttp的口碑相当好,介绍一下OKHttp的安装及使用: 一.安装 对于Android Studio的用户,在Projec ...
- 初识Tensorboard
1.什么是Tensorboard? PPT设计原则中有这样一条,叫"文不如表,表不如图",可见图表在表达中更为直观.明确.程序设计中也是一样,我们经常用图表来描述程序的结构和流程, ...
- 基于java.util.logging实现轻量级日志记录库(增加根据当前类class初始化,修复线程池模型(javaEE)下的堆栈轨迹顺序与当前调用方法不一致问题)
前言: 本章介绍自己写的基于java.util.logging的轻量级日志记录库(baseLog). 该版本的日志记录库犹如其名,baseLog,是个实现日志记录基本功能的小库,适合小型项目使用,方便 ...
- 搭建rtmp直播流服务之4:videojs和ckPlayer开源播放器二次开发(播放rtmp、hls直播流及普通视频)
前面几章讲解了使用 nginx-rtmp搭建直播流媒体服务器; ffmpeg推流到nginx-rtmp服务器; java通过命令行调用ffmpeg实现推流服务; 从数据源获取,到使用ffmpeg推流, ...
- Discuz插件开发之全站论坛目录结构注释
基本上新版本的discuzX系列目录结构都差不多,刚好大神整理出来了,就拿来看吧. |-- /api uc.php UCenter通信文件 |-- /api/addon ...
- FullCalendar 日历插件中文说明文档
FullCalendar提供了丰富的属性设置和方法调用,开发者可以根据FullCalendar提供的API快速完成一个日历日程的开发,本文将FullCalendar的常用属性和方法.回调函数等整理成中 ...
- Intel VT-x 处于禁用状态
出现错误"此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态"的问题,如下图. Intel VT-x 即Virtualization Technology, ...
- 关于MATLAB收集人工鼠标移动轨迹的坐标
首先需要设计一个用户图形界面的函数,这个图形界面被用于在其上面绘制轨迹并记录当时的坐标. 该回响函数应包含:鼠标按下时,鼠标移动时,和鼠标释放时的反应命令.当然网上有有相关的开源 程序,但是有缺陷(该 ...
- JavaScript面向对象轻松入门之继承(demo by ES5、ES6)
继承是面向对象很重要的一个概念,分为接口继承和实现继承,接口继承即为继承某个对象的方法,实现继承即为继承某个对象的属性.JavvaScript通过原型链来实现接口继承.call()或apply()来实 ...