FreeMarker的用法

freemark就是一个对静态页面上的标签进行动态解析、填充数据的一个框架。

  • 语法(转:http://zhuyuehua.iteye.com/blog/1975251): 

1. freemarker获取list的size :
Java

ArrayList<String> list = new ArrayList<String>();

Freemaker

${list?size}

2. list的遍历:

<#list animals as being>
<tr>
<td>${being.name}${being.price}<td>
</tr>
</#list>

3. 遍历MAP 

<#list map?keys as k>
<option value="${k}">${map[k]}</option>
</#list>

4.list遍历中的下标序号:

_index是list的一个属性

<#list list as a>
${a_index}
</#list> 

5.取LIST中第i个元素的值

${list[i]} 

嵌套时前面要有括号,如下,将字符串变成list,然后取第i个元素的值

${(str?split(","))[i]} 

6. list的嵌套:

<#list jsskList as jsskVO>
<#list kcList as kcVO>
<#if kcVO.kch=jsskVO.kch> (kcVO里有编号和名称,而jsskVO里只有编号)
${kcVO.kcm} 
</#if>
</#list>
</#list>

7. list排序:

升序 .sort_by()

<#list list?sort_by("字段") as x>
</#list>

降序 .sort_by()?reverse

<#list list?sort_by("字段")?reverse as x> 
</#list>

8.item_has_next,size使用:

<#list userList as user>
    <#if !user_has_next>
    共有${userList?size}最后一个用户是:${user.userName}
    </#if>
</#list>
  • 实际项目中的用法:


\My-SSH-Blog\WebContent\blog\template\template.ftl

  1  <!DOCTYPE HTML>
  2 <html lang="zh-CN">
  3 <head>
  4     <meta charset="UTF-8">
  5     <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1,user-scalable=no">
  6     <title>DX博客</title>
  7     <link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style.css" />
  8     <link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style/css/font-awesome.min.css" />
  9     <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,400italic,300italic,300,700,700italic|Open+Sans+Condensed:300,700" rel="stylesheet" type="text/css">
 10     <!--[if IE 7]>
 11     <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/font-awesome-ie7.min.css"/>
 12     <![endif]-->
 13     <!--[if IE 8]>
 14     <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie8.css" media="all" />
 15     <![endif]-->
 16     <!--[if IE 9]>
 17     <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie9.css" media="all" />
 18     <![endif]-->
 19     <script type="text/javascript" src="${contextPath}/style/js/jquery-1.7.2.min.js"></script>
 20     <script type="text/javascript" src="${contextPath}/style/js/ddsmoothmenu.js"></script>
 21     <script type="text/javascript" src="${contextPath}/style/js/retina.js"></script>
 22     <script type="text/javascript" src="${contextPath}/style/js/selectnav.js"></script>
 23     <script type="text/javascript" src="${contextPath}/style/js/jquery.backstretch.min.js"></script>
 24     <script type="text/javascript">
 25         $.backstretch("${contextPath}/style/images/bg/16.jpg");
 26     </script>
 27 </head>
 28 <body>
 29 <div class="scanlines"></div>
 30 
 31 <div class="header-wrapper opacity">
 32     <div class="header">
 33         <div class="logo">
 34             <a href="${contextPath}">
 35                 <h1>DX博客</h1>
 36             </a>
 37         </div>
 38 
 39         <div id="menu-wrapper">
 40             <div id="menu" class="menu">
 41                 <ul id="tiny">
 42                     <li class="active">
 43                         <a href="${contextPath}">博客</a>
 44                         <ul>
 45                             <li><a href="${contextPath}/listArticle.action">所有博文</a></li>
 46                         </ul>
 47                     </li>
 48                     <li>
 49                         <a href="#" title="进入我的Github">Github</a>
 50                     </li>
 51                     <li>
 52                         <a href="#" title="进入我的CSDN博客">CSDN博客</a>
 53                     </li>
 54                     <li>
 55                         <a href="${contextPath}/commentUI.action" title="给我留言">留言板</a>
 56                     </li>
 57                     <li><a href="">Menu</a>
 58                         <ul>
 59                             <li><a href="/s/">短网址</a></li>
 60                             <li><a href="/WorkUpload/">作业提交系统</a></li>
 61                         </ul>
 62                     </li>
 63                 </ul>
 64             </div>
 65         </div>
 66         <div class="clear"></div>
 67     </div>
 68 </div>
 69 
 70 <div class="wrapper"><!--
 71     <ul class="social">
 72         <li><a class="rss" href="#" title="博客订阅"></a></li>
 73         <li><a class="qq" href="#" title="QQ"></a></li>
 74         <li><a class="weibo" href="#" title="新浪微博"></a></li>
 75         <li><a class="wechat" href="${contextPath}/public/wechat.jsp" title="微信"></a></li>
 76         <li><a class="email" href="#" title="邮件"></a></li>
 77         <li><a class="phone" href="#" title="手机"></a></li>
 78     </ul>
 79     <div class="intro">Intro...</div>
 80 -->
 81     <div class="content box">
 82         <h1 class="title article-title">${title}</h1>
 83         <div class="info">
 84             <div><a href="${listCategoryArticle}" title="查看该类型博文"><i class="icon-folder-open-alt"></i>:${categoryName}</a></div>
 85             <div><a href=""><i class="icon-user"></i>:${author}</a></div>
 86             <div><i class="icon-time"></i>:${time}</div>
 87         </div>
 88         ${typeString}
 89         ${content}
 90         ${typeString}
 91         <div class="record">
 92             <div>
 93                 <a id="looked" href=""><i class="icon-eye-open"></i> ${looked} 已阅</a>
 94             </div>
 95             &nbsp;&nbsp;
 96             <div>
 97                 <a id="likes" href="javascript:like('${contextPath}/likeAction.action?artid=${articleId}')"><i class="icon-heart-empty"></i> ${likes} 喜爱</a>
 98             </div>
 99             <a class="comment-btn" href="javascript:onComment('#')"><i class="icon-comments"></i> 给我留言</a>
100         </div>
101         <div class="last-next">
102             <div>
103                 <a href="${lastArticle.staticUrl}" title="上一篇">
104                     <i class="icon-double-angle-left"></i>${lastArticle.title}
105                 </a>
106             </div>
107             <div>
108                 <a href="${nextArticle.staticUrl}" title="下一篇">
109                     <i class="icon-double-angle-right"></i>${nextArticle.title}
110                 </a>
111             </div>
112         </div>
113     </div>
114 
115     <div class="sidebar box">
116         <div class="sidebox widget">
117             <h3 class="widget-title">最近更新</h3>
118             <ul class="post-list">
119                 <#list lastArticlesList as la>
120                     <li>
121                         <div class="meta">
122                             <h5><a href="${contextPath}${la.staticUrl}.html">${la.title}</a></h5>
123                             <em>${la.createDate?string("yyyy-MM-dd HH:mm:ss")}</em>
124                         </div>
125                     </li>
126                 </#list>
127                 <li class="more"><a href="${contextPath}/listArticle.action">more</a></li>
128             </ul>
129         </div>
130 
131         <div class="sidebox widget">
132             <h3 class="widget-title"><i class="icon-search icon"></i></h3>
133             <form class="searchform" method="post" action="#">
134                 <input type="text" name="key" value="输入关键字搜索博客..." onFocus="this.value=''" onBlur="this.value='输入关键字搜索博客...'"/>
135             </form>
136         </div>
137 
138         <div class="sidebox widget">
139             <h3 class="widget-title categories">分类</h3>
140             <ul class="categories">
141                 <#list categoryList as cl>
142                     <li><a href="${contextPath}/listArticle.action?categoryId=${cl.id}">${cl.title}</a></li>
143                 </#list>
144             </ul>
145         </div>
146     </div>
147 
148     <div class="clear"></div>
149 
150 </div>
151 
152 <div class="footer-wrapper">
153     <div id="footer" class="four">
154         <a href="/s/">短网址</a>&nbsp;&nbsp;
155         <a href="/WorkUpload/">作业提交系统</a>&nbsp;&nbsp;
156     </div>
157 </div>
158 <div class="site-generator-wrapper">
159     <div class="site-generator">
160         Copyright &copy;2017.DX &nbsp;Design by <a href="http://elemisfreebies.com">elemis.</a> All rights reserved.
161     </div>
162 </div>
163 <script type="text/javascript" src="${contextPath}/style/js/scripts.js"></script>
164 <script type="text/javascript">
165     function onComment(url){
166         var form = document.createElement('form');
167         form.action = url;
168         form.method = "post";
169         form.style.display =  "none";
170 
171         var input = document.createElement("input");
172         input.name = "title";
173         input.value = "${title}";
174         form.appendChild(input);
175 
176         var input2 = document.createElement("input");
177         input2.name = "articleId";
178         input2.value = "${articleId}";
179         form.appendChild(input2);
180 
181         document.body.appendChild(form);
182         form.submit();
183     }
184 </script>
185 </body>
186 </html>
View Code

我访问的对应网址:http://localhost:8080/My-SSH-Blog/blog/5/5-4.html,呈现效果:

这里我使用的是一个url重写的方式实现的,因此这里博客文章页面的template.ftl的解析过程是在filter中实现的。

web.xml中定义了filter过滤规则:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://java.sun.com/xml/ns/javaee"
 4     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 5     id="WebApp_ID" version="3.0">
 6     <display-name>My-SSH-Blog</display-name>
 7 
 8     <welcome-file-list>
 9         <welcome-file>/index.jsp</welcome-file>
10     </welcome-file-list>
11 
12     <error-page>
13         <error-code>404</error-code>
14         <location>/public/404.html</location>
15     </error-page>
16     <error-page>
17         <error-code>500</error-code>
18         <location>/public/500.html</location>
19     </error-page>
20 
21     <!-- html请求过滤器 -->
22     <filter>
23         <filter-name>articleFilter</filter-name>
24         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
25         <init-param>
26             <param-name>targetFilterLifecycle</param-name>
27             <param-value>true</param-value>
28         </init-param>
29     </filter>
30     <filter-mapping>
31         <filter-name>articleFilter</filter-name>
32         <url-pattern>*.html</url-pattern>
33     </filter-mapping>
34     <filter-mapping>
35         <filter-name>articleFilter</filter-name>
36         <url-pattern>*.htm</url-pattern>
37     </filter-mapping>
38 
39     <context-param>
40         <param-name>contextConfigLocation</param-name>
41         <param-value>classpath:applicationContext*.xml</param-value>
42     </context-param>
43 
44     <!-- 监听session,在线人员信息(登录账户,不包括访客和前台在线用户) -->
45     <listener>
46         <listener-class>com.dx.ssh.listener.SessionListener</listener-class>
47     </listener>
48     <!-- Bootstraps the root web application context before servlet initialization -->
49     <listener>
50         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
51     </listener>
52 
53     <!-- Struts2 filter -->
54     <filter>
55         <filter-name>struts2</filter-name>
56         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
57     </filter>
58 
59     <filter-mapping>
60         <filter-name>struts2</filter-name>
61         <url-pattern>/*</url-pattern>
62     </filter-mapping>
63 
64     <session-config>
65         <session-timeout>30</session-timeout>
66     </session-config>
67 </web-app>
View Code

ArticleFilter.java

  1 package com.dx.ssh.filter;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.util.Map;
  6 import java.util.regex.Matcher;
  7 import java.util.regex.Pattern;
  8 
  9 import javax.servlet.*;
 10 import javax.servlet.http.HttpServletRequest;
 11 import javax.servlet.http.HttpServletResponse;
 12 import javax.servlet.http.HttpSession;
 13 
 14 import org.springframework.web.context.WebApplicationContext;
 15 import org.springframework.web.context.support.WebApplicationContextUtils;
 16 
 17 import com.dx.ssh.service.ArticleService;
 18 import com.dx.ssh.utils.TemplateUtils;
 19 import com.opensymphony.xwork2.ActionContext;
 20 
 21 public class ArticleFilter implements Filter {
 22     public static final String ARTICLE_TOKEN = "view";
 23     private ArticleService articleService;
 24 
 25     public ArticleService getArticleService() {
 26         return articleService;
 27     }
 28 
 29     public void setArticleService(ArticleService articleService) {
 30         this.articleService = articleService;
 31     }
 32 
 33     // protected Object getBean(ServletContext servletContext, String id) {
 34     // WebApplicationContext context =
 35     // WebApplicationContextUtils.getWebApplicationContext(servletContext);
 36     // return context.getBean(id);
 37     // }
 38 
 39     @Override
 40     public void init(FilterConfig filterConfig) throws ServletException {
 41     }
 42 
 43     @Override
 44     public void destroy() {
 45     }
 46 
 47     @Override
 48     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
 49             throws IOException, ServletException {
 50         HttpServletRequest request = (HttpServletRequest) servletRequest;
 51         HttpServletResponse response = (HttpServletResponse) servletResponse;
 52 
 53         // ArticleService articleService = (ArticleService)
 54         // this.getBean(request.getServletContext(), "articleService");
 55 
 56         String path = request.getRequestURL().toString();
 57         // 模式匹配
 58         Pattern pattern = Pattern.compile("/blog/([0-9]+)/([0-9]+)-([0-9]+)");
 59         Matcher matcher = pattern.matcher(path);
 60 
 61         // 不匹配,路径不正确
 62         if (!matcher.find()) {
 63             // 路径不对,报错404
 64             response.sendError(404, "您输入路径的不存在!");
 65             return;
 66         }
 67         // 模版文件绝对路径
 68         String realPath = request.getServletContext().getRealPath("/");
 69         // 文件路径
 70         String filePath = realPath + matcher.group() + ".ftl";
 71 
 72         filePath = realPath + matcher.group() + ".ftl";
 73         filePath = realPath + "blog/template/template.ftl";
 74         filePath = filePath.replace("/", "\\");
 75         filePath = filePath.replace("\\\\", "\\");
 76 
 77         System.out.println(filePath);
 78         // 查看服务器资源是否存在
 79         File file = new File(filePath);
 80         if (!file.exists()) {
 81             // 路径不对,报错404
 82             response.sendError(404, "您输入路径的不存在!");
 83             return;
 84         }
 85         // 解析路径中的文章id
 86         int articleId = Integer.parseInt(matcher.group(3));
 87         System.out.println("articleId = " + articleId);
 88 
 89         // 防止同一用户session添加多次访问量
 90         boolean isNew = false;
 91         // 获取当前用户session
 92         HttpSession session = request.getSession();
 93         // 还没看过就能添加访问量
 94         if (session.getAttribute(ARTICLE_TOKEN + articleId) == null) {
 95             isNew = true;
 96             // 设置当前的用户session已经看过文章了
 97             session.setAttribute(ARTICLE_TOKEN + articleId, "true");
 98         }
 99 
100         // 填充模版信息
101         Map<String, Object> params = null;
102         try {
103             // 得到freemarker模版文件所需参数
104             params = this.getArticleService().getTemplateParams(articleId, request.getContextPath(), isNew);
105             params.put("contextPath", request.getContextPath());
106         } catch (Exception e) {
107             e.printStackTrace();
108         }
109 
110         String templateDir = realPath + File.separator + "blog";
111         String templateFile = matcher.group(1) + "/" + matcher.group(2) + "-" + matcher.group(3) + ".ftl";
112         templateFile = "template/template.ftl";
113         boolean result = TemplateUtils.parserTemplate(templateDir, templateFile, params, response.getOutputStream());
114 
115         if (!result) {
116             // 服务器异常
117             response.sendError(500, "服务器未知异常!");
118         }
119         response.getOutputStream().close();
120     }
121 
122 }
View Code

TemplateUtils.java

 1 package com.dx.ssh.utils;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.io.OutputStream;
 6 import java.io.OutputStreamWriter;
 7 import java.util.Map;
 8 
 9 import freemarker.template.Configuration;
10 import freemarker.template.Template;
11 
12 public class TemplateUtils {
13     private static Configuration configuration = null;
14 
15     private TemplateUtils() {
16     }
17 
18     public static Configuration getConfiguration(String templateDir) throws IOException {
19         if (configuration == null) {
20             configuration = new Configuration();
21             configuration.setDirectoryForTemplateLoading(new File(templateDir));
22         }
23         return configuration;
24     }
25 
26     /**
27      * 向ftl模版中的数据替换
28      * */ 
29     public static boolean parserTemplate(String templateDir, String ftlPath, Map<String, Object> map, OutputStream os) {
30         try {
31             Template template = getConfiguration(templateDir).getTemplate(ftlPath, "UTF-8");
32             template.process(map, new OutputStreamWriter(os, "UTF-8"));
33             return true;
34         } catch (Exception e) {
35             e.printStackTrace();
36             return false;
37         }
38     }
39 }

模版填充数据类型整理方法:

 1 /**
 2      * 得到freemarker模版文件所需参数
 3      */
 4     @Override
 5     public Map<String, Object> getTemplateParams(int articleId, String contextPath, boolean isNew) {
 6         System.out.println("articleId=" + articleId);
 7         // 要看的文章
 8         ArticleEntity article = getArticleDao().findById(articleId);
 9 
10         if (article == null || article.getId() <= 0)
11             return null;
12 
13         // 最新三篇文章
14         List<ArticleEntity> lastArticles = getArticleDao().findAllLastArticle(3);
15 
16         // 所有类别
17         List<ArticleCategoryEntity> categories = getArticleCategoryDao().findAll();
18 
19         // 下一篇
20         ArticleEntity next = null;
21         List<ArticleEntity> nextArticles = articleDao.findNextArticle(3, article.getCreateDate());
22         if (nextArticles == null || nextArticles.size() <= 0) {
23             next = new ArticleEntity();
24             next.setStaticUrl("#");
25             next.setTitle("这是最后一篇了哦!");
26         } else {
27             next = nextArticles.get(0);
28             next.setStaticUrl(contextPath + next.getStaticUrl() + ".html");
29         }
30 
31         // 上一篇文章
32         ArticleEntity last = null;
33         List<ArticleEntity> lastAs = articleDao.findLastArticle(3, article.getCreateDate());
34         if (lastAs == null || lastAs.size() <= 0) {
35             last = new ArticleEntity();
36             last.setStaticUrl("#");
37             last.setTitle("这是第一篇哦!");
38         } else {
39             last = lastAs.get(0);
40             last.setStaticUrl(contextPath + last.getStaticUrl() + ".html");
41         }
42 
43         java.text.SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
44         // 封装模版所需参数
45         Map<String, Object> params = new HashMap<String, Object>();
46         params.put("looked", article.getLooks());
47         params.put("articleId", article.getId());
48         params.put("time", format.format(article.getCreateDate()));
49         params.put("title", article.getTitle());
50         params.put("content", article.getContent());
51         params.put("typeString", "------------------------------------------------------");
52         params.put("author", article.getAuthor());
53         params.put("categoryName", getArticleCategoryDao().findById(article.getCategoryId()).getTitle());
54         params.put("likes", article.getLikes());
55         params.put("listCategoryArticle", contextPath + "/listArticle.action?categoryId=" + article.getCategoryId());
56         params.put("lastArticlesList", lastArticles);
57         params.put("categoryList", categories);
58         params.put("likesURL", contextPath + "/likeAction.action?articleId=" + article.getId());
59         params.put("nextArticle", next);
60         params.put("lastArticle", last);
61         params.put("staticURL", article.getStaticUrl());
62 
63         return params;
64     }

 

posted @ 2017-07-27 13:45  cctext  阅读(1806)  评论(0编辑  收藏  举报