一、首先复习下velocity模板的使用
package com.springmvc.controller;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
public class VelocityTest {
public static void main(String[] args) throws Exception {
VelocityContext context = new VelocityContext();
fillData(context);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
new VelocityEngine().getTemplate("/src/main/resources/test.vm").merge(context, writer);
writer.flush();
writer.close();
}
static void fillData(VelocityContext context){
//填充list
List<String> list = new ArrayList<String>();
list.add("list-a");
list.add("list-b");
list.add("list-c");
context.put("list", list);
//填充map
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("key1", "map-val1");
map.put("key2", "map-val2");
map.put("key3", "map-val3");
context.put("map", map);
//填充JavaBean
List<User> userList = new ArrayList<User>();
userList.add(new User("username1", 18)) ;
userList.add(new User("username2", 19));
context.put("userlist", userList);
}
}
package com.springmvc.controller;
public class User {
private String name;
private int age;
public User(){
}
public User(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/src/main/resources/test.vm文件:
list:
#foreach($str in $list)
${str}
#end
map:
#foreach($ma in $map.entrySet())
$!{ma.key} ${ma.value}
#end
bean:
#foreach($user in $userlist)
$!{user.name}
$!{user.age}
#end
控制台输出:
list:
list-a
list-b
list-c
map:
key1 map-val1
key2 map-val2
key3 map-val3
bean:
username1
18
username2
19
二、SpringMVC整合velocity
1.首先看下springMVC整合velocity的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!-- 扫描controller -->
<context:component-scan base-package="com.springmvc.controller" />
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".vm" />
<property name="contentType">
<value>text/html;charset=UTF-8</value>
</property>
</bean>
<bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/velocity/" />
<property name="velocityProperties">
<props>
<prop key="input.encoding">utf-8</prop>
<prop key="output.encoding">utf-8</prop>
</props>
</property>
</bean>
</beans>
其中类VelocityViewResolver是velocity的视图解析类,用于生成velocity视图类,该类最调用velocity的api解析vm文件,并将Controler传入的map对象(模型)传入velocity,类VelocityConfigurer是velocity的配置类。
1)DispatcherServlet取得ModelAndView:
由SpringMVC的核心DispatcherServlet收到请求,其service方法经过一层层调用,找到请求处理适配器HandlerAdapter(本例中是RequestMappingHandlerAdapter),调用对应的类的相应方法,最终得到一个ModelAndView类。
2)取得VelocityViewResolver:
DispatcherServlet继续调用processDispatchResult方法,该方法调用void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) 方法,render方法调用resolveViewName方法,该方法取得配置中的VelocityViewResolver,
3) VelocityView视图类的生成:
VelocityViewResolver调用基类的
AbstractUrlBasedView buildView(String viewName)
------>AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
方法,生成VelocityView类。
其中viewClass是VelocityViewResolver在构造方法中调用父类的方法setViewClass初始化的。
public VelocityViewResolver() {
setViewClass(requiredViewClass());
}
/**
* Requires {@link VelocityView}.
*/
@Override
protected Class requiredViewClass() {
return VelocityView.class;
}
4) velocity页面渲染:
VelocityView类调用父类的render方法,方法签名是:
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception
render方法调用VelocityView实现的renderMergedTemplateModel方法:
@Override
protected void renderMergedTemplateModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
exposeHelpers(model, request);
Context velocityContext = createVelocityContext(model, request, response);
exposeHelpers(velocityContext, request, response);
exposeToolAttributes(velocityContext, request);
doRender(velocityContext, response);
}
renderMergedTemplateModel方法继续调用doRender方法:
protected void doRender(Context context, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Rendering Velocity template [" + getUrl() + "] in VelocityView '" + getBeanName() + "'");
}
mergeTemplate(getTemplate(), context, response);
}
其中mergeTemplate方法实现如下:
protected void mergeTemplate(
Template template, Context context, HttpServletResponse response) throws Exception {
try {
template.merge(context, response.getWriter());
}
catch (MethodInvocationException ex) {
Throwable cause = ex.getWrappedThrowable();
throw new NestedServletException(
"Method invocation failed during rendering of Velocity view with name '" +
getBeanName() + "': " + ex.getMessage() + "; reference [" + ex.getReferenceName() +
"], method '" + ex.getMethodName() + "'",
cause==null ? ex : cause);
}
}
以上的步骤简要概括如下:
i)通过createVelocityContext方法生成VelocityContext对象,其构造参数是Map model;
ii)通过getTemplate方法取得Template对象,该对象是使用VelocityEngine的getTemplate方法生成,而VelocityEngine则是VelocityView(实现了ApplicationContextAware接口)通过spring容器经VelocityConfigurer取得,VelocityEngine是VelocityConfigurer类在afterPropertiesSet中初始化的成员变量。
iii)通过template.merge(context, response.getWriter())方法将页面通过response的writer进行输出。
至此,整个velocity渲染的过程结束。
总结:
1.springMVC和velocity整合点是在DispatcherServlet的resolveViewName方法,通过spring配置中的的ViewResolver类,生成velocity的视图类VelocityView(包含视图模板的文件名),视图的渲染是在该类中进行的;
2.通过springMVC的Controller生成的ModelMap对象创建velocity的Context对象;
3.VelocityView实现了ApplicationContextAware接口,通过Spring容器,经spring配置文件中定义的VelocityConfig的实现,得到VelocityEngine,VelocityEngine生成Template对象。
4.通过VelocityView得到视图对应的文件名,通过HttpServletResponse对象得到输出Writer,调用Template的merge( Context context, Writer writer)方法,输出页面。