前不久项目中遇到一个问题:要求生成静态页面。
于是想到了velocity和FreeMarker。。。。
这是两个JAVA模板引擎,作为当前流行的几大MVC框架的有益补充,受到了开源框架Spring的支持。
下面总结一下初步FreeMarker应用。
1。要和Spring结合,首先要在Spring的Context中注册org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer的一个实例。
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/template/"/>
</bean>
2。编写模板:
比如,编写一个生成SQL脚本的模板。
<#if genreList?exists>
<#list genreList as commongenre>
INSERT INTO test1.tb_commongenre (vc2genrename) VALUES ('${commongenre?js_string}');
</#list>
</#if>
<#if cataList?exists>
<#list cataList as vc2catagorytagname>
INSERT INTO test1.tb_catagorytags (vc2catagorytagname) VALUES ('${vc2catagorytagname?js_string}');
</#list>
</#if>
<#if stdgenList?exists>
<#list stdgenList as vc2stdgenrename>
INSERT INTO test1.tb_stdgenre (vc2stdgenrename) VALUES ('${vc2stdgenrename?js_string}');
</#list>
</#if>
3:编写代码来生成最终的脚本。
public class MusicSqlGenerator {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml"});
private String tablename;
private String basePath = "/home/x-spirit";
public void genSql() throws Exception{
FreeMarkerConfigurer freemarkerConfig = (FreeMarkerConfigurer)ctx.getBean("freemarkerConfig");
Template tpl =freemarkerConfig.getConfiguration().getTemplate("newSQLTemplate.sql");
Map map = new HashMap();
map.put("stdgenList",readToList(new ArrayList<String>(), "tb_stdgenre",true,-1,-1));
String context = FreeMarkerTemplateUtils
.processTemplateIntoString(tpl, map);
PrintWriter pw = new PrintWriter(new FileOutputStream("c:/target.sql"));
pw.println(context);
pw.flush();
pw.close();
}
public String[] readToList(List<String> list,String tablename,boolean all,int start,int limit)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(basePath+File.separator+tablename), "UTF-8"));
String s = null;
int i = 0;
int j = 0;
while((s=br.readLine())!=null){
if(all){
list.add(s);
}else{
if(++i>=start){
if(++j<=limit){
list.add(s);
}
}
}
}
String[] arr = list.toArray(new String[list.size()]);
return arr;
}
public static void main(String args[]){
try {
new MusicSqlGenerator().genSql();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
结果,当tb_stdgenre文件中包含将近100MB的数据时,程序出现堆内存泄露。。。因为这100MB的数据要在一个String对象里面存储实在是太多了。。。
于是研究了Spring的源代码和FreeMarker的源代码,对程序做了如下改进:
首先继承Spring里面的FreeMarkerTemplateUtils。这个类里面是把StringWriter传进去,最后返回一个String。。。这就是罪魁祸首阿。。那我直接把用于磁盘I/O的PrintWriter传进去好了,这样至少不会瞬间在内存里堆积大量的数据。
public class FreeMarkerTemplateUtils extends org.springframework.ui.freemarker.FreeMarkerTemplateUtils{
public static void processTemplateIntoFile(Template template, Object model,String filepath)
throws IOException, TemplateException {
PrintWriter pw = new PrintWriter(new FileOutputStream(filepath));
template.process(model, pw);
}
}
注意这段代码后面不需要对pw对象做flush()和close()。因为FreeMarker里面已经做过了。不相信的读者请自己去看源码。
接下来对原来的程序进行改造:
public class MusicSqlGenerator {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml"});
private String tablename;
private String basePath = "/home/x-spirit";
public void genSql() throws Exception{
FreeMarkerConfigurer freemarkerConfig = (FreeMarkerConfigurer)ctx.getBean("freemarkerConfig");
Template tpl =freemarkerConfig.getConfiguration().getTemplate("newSQLTemplate.sql");
Map map = new HashMap();
map.put("stdgenList",readToList(new ArrayList<String>(), "tb_stdgenre",true,-1,-1));
FreeMarkerTemplateUtils
.processTemplateIntoFile(tpl, map,"/home/x-spirit/results/musicStyle.sql");
}
public String[] readToList(List<String> list,String tablename,boolean all,int start,int limit)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(basePath+File.separator+tablename), "UTF-8"));
String s = null;
int i = 0;
int j = 0;
while((s=br.readLine())!=null){
if(all){
list.add(s);
}else{
if(++i>=start){
if(++j<=limit){
list.add(s);
}
}
}
}
String[] arr = list.toArray(new String[list.size()]);
return arr;
}
public static void main(String args[]){
try {
new MusicSqlGenerator().genSql();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
好了。先写到这里。。。
总之,感觉Spring对FreeMarker的支持其实还不是特别的好,就是简单封装了一下。不过至少也为我们使用提供了参照范例。感谢Juergen Hoeller的启发。