利用FreeMarker模板引擎创建动态调用

作者:胡志刚 发布时间:June 29, 2009 分类:所谓技术

比较郁闷,这个标题不知道怎么起,以前写过关于FreeMarker生成静态页面,还有调用的一些文章,但这次好像又稍微有点不同,说不同呢,又差不多,搞得都不知道自己在捣鼓些什么了,- -#,暂且就这样说好了。

自己平常在用到一些程序的时候,比较喜欢关注程序的模板方面,现在大部分的程序都能实现了灵活的模板设计,将模板和程序分离开来,一直都在想在java下的解决方案,每次想到这块,脑子里第一就会想到FreeMarker,总感觉能实现,但自己又说不清楚怎么来实现,正好,今天有点时间,抽空自己写了个简单的实力来实现这个功能,就类似于说,我可以自己定义一个方法的调用,然后决定我调用多少条文章,是否调用置顶了之类的东东,唉,说是不好说,直接来代码好了。

主要的文件一共有三个,一个Servlet,一个标记解析的类,一个模板文件,代码如下:

package com.fm.study;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * 访问的Servlet
 * 
 * @author HuZhigang
 * @date 2009-6-29
 */

public class FMDemo extends HttpServlet {

    /**
     * 
     */
    private static final long serialVersionUID = 8295505224176026191L;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    @SuppressWarnings("unchecked")
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            response.setContentType("text/html");
            response.setCharacterEncoding("utf-8");
            PrintWriter out = response.getWriter();
            Configuration cfg = new Configuration();
            //设置FreeMarker需要读取的模板路径
            String path = this.getServletContext().getRealPath("/")
                    + "WEB-INF\\ftl";
            cfg.setDirectoryForTemplateLoading(new File(path));
            cfg.setObjectWrapper(new DefaultObjectWrapper());
            cfg.setDefaultEncoding("utf-8");
            //模板文件名
            Template temp = cfg.getTemplate("index.ftl");
            Map root = new HashMap();
            //写入一个变量,key对应value
            root.put("user", "Hmilyld");
            //创建一个回调变量
            root.put("call", new CallFactory());
            temp.process(root, out);
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }
}

代码很简单,主要就是读取模板文件然后输出就可以了,主要注意的是这句:root.put("call", new CallFactory());我们定义一个变量,然后可以调用我们的方法。

下面是我们需要操作的方法,CallFactory需要实现TemplateMethodModel这个接口,然后我们所传入的参数以List的形式传入,这个时候,可以读取List来获取我们相关传入的参数,代码如下:

package com.fm.study;

import java.util.ArrayList;
import java.util.List;

import com.fm.bean.ArticleBean;
import com.fm.bean.ProductBean;

import freemarker.template.TemplateMethodModel;
import freemarker.template.TemplateModelException;

/**
 * 解析标记的相关方法
 * 
 * @author HuZhigang
 * @date 2009-6-29
 */

public class CallFactory implements TemplateMethodModel {

    @SuppressWarnings("unchecked")
    public Object exec(List arg0) throws TemplateModelException {
        String type = (String) arg0.get(0);
        if ("article".equals(type)) {
            // 如果标记为article,则调用文章的相关方法
            return article(arg0);
        } else if ("product".equals(type)) {
            // 如果标记为product,则调用商品的相关方法
            return product(arg0);
        } else {
            throw new TemplateModelException("Error!");
        }
    }

    /**
     * 调用文章的相关方法
     * 
     * @return
     * @author HuZhigang
     */
    private Object article(List<String> param) {
        List<ArticleBean> list = new ArrayList<ArticleBean>();
        int count = Integer.parseInt(param.get(1));
        for (int i = 0; i < count; i++) {
            ArticleBean ab = new ArticleBean();
            ab.setTitle("这里是标题--" + i);
            ab.setContent("这里是内容--" + i);
            ab.setAuthor("这里是作者" + i);
            ab.setUrl("这里是url--" + i);
            list.add(ab);
        }
        return list;
    }

    /**
     * 调用产品的相关方法
     * 
     * @return
     * @author HuZhigang
     */
    private Object product(List<String> param) {
        List<ProductBean> list = new ArrayList<ProductBean>();
        int count = Integer.parseInt(param.get(1));
        for (int i = 0; i < count; i++) {
            ProductBean pb = new ProductBean();
            pb.setName("这里是商品标题--" + i);
            pb.setMeta("这里是商品简介--" + i);
            pb.setPrice((float) Math.random());
            list.add(pb);
        }
        return list;
    }

}

OK,主要实现的功能都在这个里面了,当然,你可以完全拆分开,不同的类来实现不同的方法,具体应用就看自己怎么用了,可以按照传入的条件判断需要调用多少内容,是否固定,是否推荐等等内容

下面就是我们最后的模板文件,很简单

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>FreeMarker标记解析演示</title>
<style>
body{
    font-size:12px;
}
#article,#product{
    float:left;
    margin:5px;
}
td{
    width:120px;
}
</style>
</head>
<body>
<h1>Hello,${user}</h1>
<div id="main">
  <div id="article">
    <h2>文章列表演示</h2>
    <#--根据创建的call,调用我们需要调用的方法,我们这里传入article类型,然后数量为20条 -->
    <#assign article = call("article",20,"true")>
    <table border="1">
      <tr>
        <td>标题</td>
        <td>内容</td>
        <td>作者</td>
        <td>url</td>
      </tr>
      <#--循环打印出所有内容-->
      <#list article as art>
      <tr>
        <td>${art.title}</td>
        <td>${art.content}</td>
        <td>${art.author}</td>
        <td>${art.url}</td>
      </tr>
      </#list>
    </table>
  </div>
  <div id="product">
    <h2>商品列表演示</h2>
    <#--调用类型为商品,20条数据-->
    <#assign product = call("product",20)>
    <table border="1">
      <tr>
        <td>名称</td>
        <td>价格</td>
        <td>简介</td>
      </tr>
      <#list product as pro>
      <tr>
        <td>${pro.name}</td>
        <td>${pro.price}</td>
        <td>${pro.meta}</td>
      </tr>
      </#list>
    </table>
  </div>
</div>
</body>
</html>

OK,搞定,主要就是这么多的操作,还有其他的一些高级的应用,根据实际情况来自己修改,偶也没深入的去研究,只是翻开FreeMarker的API来看了下,大致的一个功能实现,嘿,对后面自己需要写的一个东西是用处大大啊。

标签: java, freemarker

添加新评论 »