Velocity源码分析
velocity模板渲染的步骤:
1) 首先初始化启动Velocity引擎,可以通过Velocity.init()或者新建VelocityEngine类,并调用其中的init()方法;
2) 创建一个VelocityContext对象,将变量名与值关联起来,与HashMap对象相类似。可以直接将值传递给页面进行引用;
3) 获取一个模板,利用Velocity.getTemplate()获取一个渲染模板,即要将数据最终渲染在哪个页面上去。
4) 创建一个输出流,将上述创建的数据最终渲染到模板上,采用的方法template.merge()
代码示例:
try {
Velocity.init("velocity.properties");
VelocityContext context = new VelocityContext();
String templateFile = "template.vm";
context.put("paramObject", "onlyone");
Template template = null;
template = Velocity.getTemplate(templateFile);
BufferedWriter writer = new BufferedWriter(new FileWriter("velocity.data"));
if (template != null) template.merge(context, writer);
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
原理分析:
整体工作流程图:
1. Velocity引擎初始化及启动
Velocity引擎在启动时,无论是采用Velocity.init()还是采用新建VelocityEngine的方式均会调用RuntimeInstance类中的init方法,通过设置的引擎属性初始化引擎,包括国际化支持,ResourceLoader设置,字符编码来完成一些属性资源及原始指令的加载工作。
1)initializeProperties()方法:初始化velocity的全局属性信息,即velocity.properties中配置的信息。首先会加载velocity自身所带的一个全局velocity.properties文件(org\apache\velocity\runtime\defaults\velocity.properties),若用户自己有写velocity.properties文件,则会将其中已经加载的一些属性覆盖掉。
2)initializeLog()方法:初始化velocity的日志信息。
3)initializeResourceManager()方法:初始化velocity的资源管理。
4)initializeDirectives()方法:初始化velocity中的语法法则。
5)initializeEventHandlers()方法:初始化事件句柄。
6)initializeParserPool()方法:初始化velocity的解析工具。
7)initializeIntrospection()方法:根据所获取到的uberspect名称实例化类。
8)vmFactory.initVelocimacro()方法:实例化VM工厂,它将从运行时中获取属性值,并初始化所有的宏。
2. 查找并解析模板
通过资源加载器(ResourceLoader)将模板文件加载到内存(转化为InputStream),然后通过AST(Abstract Syntax Tree)解析器将InputStream解析为一个AST。上面的代码,当执行Velocity.getTemplate("template.vm")时,首先通过ResourceLoader将以字节流的形式加载template.vm文件,然后通过Parser生成如下Token集合:{[<html> <body> Hello], [$foo], [world! </body> </html> ]},可以发现velocity根本不关心模板最终要渲染出来的是html还是其他什么东西,在此处对于velocity而言html标签意味着就是纯文本标签。最终构建的AST如下:
根节点下有三个子节点:
[<html> <body> Hello] 对应ASTText节点(纯文本节点)
[$foo] 对应ASTReference节点(需要替换的引用节点)
[world! </body> </html> ] 对应ASTText节点 (纯文本节点)
Velocity引擎在这里有个优化策略,可以针对生成的语法树进行cache
深入分析:
- Velocity为其模板语法定义了一份jjt文件,根据这份jjt文件,使用JJTree生成一个语法解析器。
- Velocity将模板解析的过程完全交给了语法解析器,调用解析器的parse方法直接得到AST,这一棵AST的每一个节点都对应一个SimpleNode的子类,其中不同的语法元素对应的不同的SimpleNode,比如#if条件表达式对应的SimpleNode是ASTIfStatement,而#stop指令对应的SimpleNode是ASTStop。
- 得到了AST以后,模板的渲染就比较简单了,无非就是递归地调用各个节点的SimpleNode的render方法来完成模板的渲染过程。
在了解了模板的渲染过程后,那模板上的方法是如何在渲染过程中执行的?对于Velocity里面的引用,比如$Person这样的,最后都被解析成AST中的ASTReference节点,而对于$Person.name这样的,ASTReference下面有一个ASTIdentifier节点,$Person.saySomething()这样的,ASTReference下面有一个ASTMethod节点。不论是ASTMethod还是ASTIdentifier,最后都是通过Uberspect和Introspector这两个类来完成对方法的查找,最后调用各种Executor来实现对方法的调用。
Uberspect这个类的功能是通过反射(Reflection)和内省(Introspection)来完成对需要调用的方法的获取的,而Introspector这个类的功能是根据方法名和方法参数在一个类中查找Method对象。另外,为了提高性能,会对Method的数据进行了缓存(见IntrospectionCacheData,IntrospectorCache和IntrospectorCacheImpl三个类),以便下次快速可以找到。
找到Method以后,具体的方法的执行由各个Executor控制,每一个Executor都继承了AbstractExecutor,给外部提供统一的接口去调用。
3.上下文VelocityContext
将k--v属性对注入到上下文参数中
VelocityContext context = new VelocityContext();
context.put("paramObject", "onlyone");
4. 模板渲染,输出字符流
Spring MVC是基于DispatcherServlet拦截请求并找到相应的控制器进行业务逻辑处理。Velocity与Spring整合主要需要配置的一些文件。
<!--Spring 服务层的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param> <!--Spring 容器启动监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- Spring MVC 的Servlet,它将加载WEB-INF/springDispatcher-servlet.xml 的配置文件,以启动Spring MVC模块-->
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/springDispatcher-servlet.xml
</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
springDispatcher-servlet.xml中的配置信息:
<--1、对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="allowSessionOverride" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="cache" value="true"/>
<property name=”prefix” value=”/WEB-INF/templates/”/>
<property name="suffix" value=".vm"/>
<property name="contentType">
<value>text/html;charset=UTF-8</value>
</property>
</bean> <--2、velocity的一些设置 -->
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath">
<value>velocity/</value>
</property>
<property name="velocityProperties">
<props>
<prop key="input.encoding">UTF-8</prop>
<prop key="output.encoding">UTF-8</prop>
<prop key="contentType">text/html;charset=UTF-8</prop>
</props>
</property>
</bean>
1处的工作即是定义模型视图名称的解析规则,这里我们采用的是velocity模板视图解析器,这样Spring就与velocity模板技术整合起来了。Spring的Dispatcher中有一个属性为viewResolver,通过它来寻找真正的视图对象。
2处定义了velocity的一些属性配置,包括资源的加载路径和页面的编码格式等。
参考资料:
velocity引擎源码学习一:http://bojiang.iteye.com/blog/1159313
velocity中的Introspection介绍:http://bojiang.iteye.com/blog/1159293
velocity的优化记录:http://agapple.iteye.com/blog/1051724
Velocity源码分析的更多相关文章
- [Android实例] Scroll原理-附ScrollView源码分析
想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内 ...
- BIZ中model.getSql源码分析
功能:根据model.xml文件中配置的sql,获取对应的动态sql结果. 实例代码:String sql1 = model.getSql(dao.dbMeta());String sql2 = mo ...
- springMVC源码分析--视图View(一)
之前的博客springMVC源码分析--HttpMessageConverter数据转化(一)中我们已经介绍了数据返回值的处理,在博客springMVC源码分析--ViewResolver视图解析器( ...
- Facebook Rebound 弹性动画库 源码分析
Rebound源码分析 让动画不再僵硬:Facebook Rebound Android动画库介绍一文中介绍了rebound这个库. 对于想体验一下rebound的效果,又懒得clone和编译代码的, ...
- [Android实例] Scroll原理-附ScrollView源码分析 (转载)
想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内 ...
- 精尽Spring MVC源码分析 - 寻找遗失的 web.xml
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
随机推荐
- 破解EXCEL2007的密码
破解EXCEL2007的密码 xshzhao (斑竹)顶楼举报 我有一个EXCEL2007文件(后缀是XLSX),由于设置了打开密码.现在密码搞忘了,这个文件对我很重要. 我试过了Office Pas ...
- 一个Bootstrap风格的分页控件
http://www.cnblogs.com/wangwei123/p/3682626.html 主题 jQueryBootstrap 一个Bootstrap风格的分页控件,对于喜欢Bootstr ...
- 【网络流24题】 No.15 汽车加油行驶问题 (分层图最短路i)
[题意] 问题描述:给定一个 N*N 的方形网格,设其左上角为起点◎, 坐标为( 1, 1), X 轴向右为正, Y轴向下为正, 每个方格边长为 1, 如图所示. 一辆汽车从起点◎出发驶向右下角终点▲ ...
- CAS单点登录配置[1]:准备工作
关于CAS是什么这里就不在赘述,网友将它比喻成旅游景点的套票,买了一个套票就可以观看所有景点,不需要一个景点买一次票...我们重点介绍CAS单点登录的配置. 工具/原料 1.配置好JDK环境,否则不方 ...
- iOS开发网络篇—多线程断点下载
iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...
- C#判断操作系统类型汇总
Windows操作系统的版本号一览 操作系统 PlatformID 主版本号 副版本号 Windows95 1 4 0 Windows98 1 4 10 WindowsMe 1 4 90 Window ...
- Linux本地无法登录,远程却可以登录
[root@oraserver ~]# vi /etc/pam.d/login 将以下内容注释掉: #session required /lib/security/pam_limits. ...
- bzoj1559
自动机上状压dp,把单词是否存在压成二进制位注意这里面某些单词会包含其他单词,所以某些自动机上有些状态点对应多个二进制位方案只要再顺着有方案的状态搜一遍即可 ..,'a'..'z'] of longi ...
- WordPress RokIntroScroller插件‘thumb.php’多个安全漏洞
漏洞名称: WordPress RokIntroScroller插件‘thumb.php’多个安全漏洞 CNNVD编号: CNNVD-201309-383 发布时间: 2013-09-24 更新时间: ...
- 数学:lucas定理的总结
今天考试的题目中有大组合数取模,不会唉,丢了45分,我真是个弱鸡,现在还不会lucas. 所以今天看了一下,定理差不多是: (1)Lucas定理:p为素数,则有: 即:lucas(n,m,p)=c(n ...