Velocity源码分析

velocity模板渲染的步骤:

1) 首先初始化启动Velocity引擎,可以通过Velocity.init()或者新建VelocityEngine类,并调用其中的init()方法;

2) 创建一个VelocityContext对象,将变量名与值关联起来,与HashMap对象相类似。可以直接将值传递给页面进行引用;

3) 获取一个模板,利用Velocity.getTemplate()获取一个渲染模板,即要将数据最终渲染在哪个页面上去。

4) 创建一个输出流,将上述创建的数据最终渲染到模板上,采用的方法template.merge()


代码示例:

[html]  view plain copy print ?
  1. try {  
  2.             Velocity.init("velocity.properties");  
  3.             VelocityContext context = new VelocityContext();  
  4.             String templateFile = "template.vm";  
  5.             context.put("paramObject", "onlyone");  
  6.             Template template = null;  
  7.             template = Velocity.getTemplate(templateFile);  
  8.             BufferedWriter writer = new BufferedWriter(new FileWriter("velocity.data"));  
  9.             if (template != null) template.merge(context, writer);  
  10.             writer.flush();  
  11.             writer.close();  
  12.         } catch (Exception e) {  
  13.             e.printStackTrace();  
  14.         }  


原理分析:


整体工作流程图:


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),然后通过ASTAbstract 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属性对注入到上下文参数中

[html]  view plain copy print ?
  1. VelocityContext context = new VelocityContext();  
  2. context.put("paramObject", "onlyone");  

4. 模板渲染,输出字符流
当执行template.merge(context, writer);时,模板遍历其对应的AST树,执行每个节点的渲染过程。如ASTText节点只是简单的将文本写入writer。ASTReference节点需要从context中获取引用的参数paramObject的值onlyone将${paramObject}替换,并写入到writer中。Velocity的AST中有多种节点,如ASTIdentitor等,有些需要反射机制处理。当整个AST遍历结束,也就意味着模板渲染结束,渲染的结果写入writer流中。


Spring与Velocity的整合

Spring MVC是基于DispatcherServlet拦截 请求并找到相应的控制器进行业务逻辑处理。Velocity与Spring整合主要需要配置的一些文件。
web.xml
[html]  view plain copy print ?
  1.   <!--Spring 服务层的配置文件 -->  
  2.   <context-param>                     
  3.       <param-name>contextConfigLocation</param-name>  
  4.       <param-value>classpath:applicationContext.xml</param-value>  
  5.   </context-param>  
  6.     
  7.   <!--Spring 容器启动监听器 -->  
  8.   <listener>  
  9.       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  10.   </listener>  
  11.   
  12.   <!--  Spring MVC 的Servlet,它将加载WEB-INF/springDispatcher-servlet.xml 的配置文件,以启动Spring MVC模块-->  
  13.   <servlet>  
  14. <servlet-name>springDispatcher</servlet-name>  
  15. <servlet-class>  
  16.     org.springframework.web.servlet.DispatcherServlet  
  17. </servlet-class>  
  18. <init-param>  
  19.     <param-name>contextConfigLocation</param-name>  
  20.     <param-value>  
  21.         /WEB-INF/springDispatcher-servlet.xml  
  22.     </param-value>  
  23. </init-param>  
  24.   </servlet>  
  25.   
  26.   <servlet-mapping>  
  27.       <servlet-name>springDispatcher</servlet-name>  
  28.       <url-pattern>*.do</url-pattern>  
  29.   </servlet-mapping>  
springDispatcher-servlet.xml中的配置信息:
[html]  view plain copy print ?
  1. <--1、对模型视图名称的解析,即在模型视图名称添加前后缀 -->  
  2. <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">  
  3. <property   name="allowSessionOverride" value="true"/>  
  4. <property   name="exposeSessionAttributes" value="true"/>  
  5. <property name="cache" value="true"/>  
  6. <property name=”prefix” value=”/WEB-INF/templates/”/>  
  7. <property name="suffix" value=".vm"/>  
  8. <property name="contentType">  
  9.     <value>text/html;charset=UTF-8</value>  
  10. </property>  
  11. </bean>  
  12.   
  13. <--2、velocity的一些设置 -->  
  14. <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">     
  15. <property   name="resourceLoaderPath">    
  16.         <value>velocity/</value>                            
  17. </property>  
  18. <property   name="velocityProperties">    
  19.        <props>    
  20.          <prop   key="input.encoding">UTF-8</prop>    
  21.          <prop   key="output.encoding">UTF-8</prop>  
  22.          <prop key="contentType">text/html;charset=UTF-8</prop>      
  23.        </props>    
  24. </property>   
  25. </bean>  
1处的工作即是定义模型视图名称的解析规则,这里我们采用的是velocity模板视图解析器,这样Spring就与velocity模板技术整合起来了。Spring的Dispatcher中有一个属性为viewResolver,通过它来寻找真正的视图对象。
2处定义了velocity的一些属性配置,包括资源的加载路径和页面的编码格式等。


参考资料:
Velocity内部实现的介绍: http://agapple.iteye.com/blog/1071438
velocity引擎源码学习一: http://bojiang.iteye.com/blog/1159313
velocity中的Introspection介绍: http://bojiang.iteye.com/blog/1159293
velocity的优化记录: http://agapple.iteye.com/blog/1051724
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值