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

  <!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1,user-scalable=no">
<title>DX博客</title>
<link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style.css" />
<link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style/css/font-awesome.min.css" />
<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">
<!--[if IE 7]>
<link rel="stylesheet" type="text/css" href="${contextPath}/style/css/font-awesome-ie7.min.css"/>
<![endif]-->
<!--[if IE 8]>
<link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie8.css" media="all" />
<![endif]-->
<!--[if IE 9]>
<link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie9.css" media="all" />
<![endif]-->
<script type="text/javascript" src="${contextPath}/style/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="${contextPath}/style/js/ddsmoothmenu.js"></script>
<script type="text/javascript" src="${contextPath}/style/js/retina.js"></script>
<script type="text/javascript" src="${contextPath}/style/js/selectnav.js"></script>
<script type="text/javascript" src="${contextPath}/style/js/jquery.backstretch.min.js"></script>
<script type="text/javascript">
$.backstretch("${contextPath}/style/images/bg/16.jpg");
</script>
</head>
<body>
<div class="scanlines"></div> <div class="header-wrapper opacity">
<div class="header">
<div class="logo">
<a href="${contextPath}">
<h1>DX博客</h1>
</a>
</div> <div id="menu-wrapper">
<div id="menu" class="menu">
<ul id="tiny">
<li class="active">
<a href="${contextPath}">博客</a>
<ul>
<li><a href="${contextPath}/listArticle.action">所有博文</a></li>
</ul>
</li>
<li>
<a href="#" title="进入我的Github">Github</a>
</li>
<li>
<a href="#" title="进入我的CSDN博客">CSDN博客</a>
</li>
<li>
<a href="${contextPath}/commentUI.action" title="给我留言">留言板</a>
</li>
<li><a href="">Menu</a>
<ul>
<li><a href="/s/">短网址</a></li>
<li><a href="/WorkUpload/">作业提交系统</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="clear"></div>
</div>
</div> <div class="wrapper"><!--
<ul class="social">
<li><a class="rss" href="#" title="博客订阅"></a></li>
<li><a class="qq" href="#" title="QQ"></a></li>
<li><a class="weibo" href="#" title="新浪微博"></a></li>
<li><a class="wechat" href="${contextPath}/public/wechat.jsp" title="微信"></a></li>
<li><a class="email" href="#" title="邮件"></a></li>
<li><a class="phone" href="#" title="手机"></a></li>
</ul>
<div class="intro">Intro...</div>
-->
<div class="content box">
<h1 class="title article-title">${title}</h1>
<div class="info">
<div><a href="${listCategoryArticle}" title="查看该类型博文"><i class="icon-folder-open-alt"></i>:${categoryName}</a></div>
<div><a href=""><i class="icon-user"></i>:${author}</a></div>
<div><i class="icon-time"></i>:${time}</div>
</div>
${typeString}
${content}
${typeString}
<div class="record">
<div>
<a id="looked" href=""><i class="icon-eye-open"></i> ${looked} 已阅</a>
</div>
&nbsp;&nbsp;
<div>
<a id="likes" href="javascript:like('${contextPath}/likeAction.action?artid=${articleId}')"><i class="icon-heart-empty"></i> ${likes} 喜爱</a>
</div>
<a class="comment-btn" href="javascript:onComment('#')"><i class="icon-comments"></i> 给我留言</a>
</div>
<div class="last-next">
<div>
<a href="${lastArticle.staticUrl}" title="上一篇">
<i class="icon-double-angle-left"></i>${lastArticle.title}
</a>
</div>
<div>
<a href="${nextArticle.staticUrl}" title="下一篇">
<i class="icon-double-angle-right"></i>${nextArticle.title}
</a>
</div>
</div>
</div> <div class="sidebar box">
<div class="sidebox widget">
<h3 class="widget-title">最近更新</h3>
<ul class="post-list">
<#list lastArticlesList as la>
<li>
<div class="meta">
<h5><a href="${contextPath}${la.staticUrl}.html">${la.title}</a></h5>
<em>${la.createDate?string("yyyy-MM-dd HH:mm:ss")}</em>
</div>
</li>
</#list>
<li class="more"><a href="${contextPath}/listArticle.action">more</a></li>
</ul>
</div> <div class="sidebox widget">
<h3 class="widget-title"><i class="icon-search icon"></i></h3>
<form class="searchform" method="post" action="#">
<input type="text" name="key" value="输入关键字搜索博客..." onFocus="this.value=''" onBlur="this.value='输入关键字搜索博客...'"/>
</form>
</div> <div class="sidebox widget">
<h3 class="widget-title categories">分类</h3>
<ul class="categories">
<#list categoryList as cl>
<li><a href="${contextPath}/listArticle.action?categoryId=${cl.id}">${cl.title}</a></li>
</#list>
</ul>
</div>
</div> <div class="clear"></div> </div> <div class="footer-wrapper">
<div id="footer" class="four">
<a href="/s/">短网址</a>&nbsp;&nbsp;
<a href="/WorkUpload/">作业提交系统</a>&nbsp;&nbsp;
</div>
</div>
<div class="site-generator-wrapper">
<div class="site-generator">
Copyright &copy;2017.DX &nbsp;Design by <a href="http://elemisfreebies.com">elemis.</a> All rights reserved.
</div>
</div>
<script type="text/javascript" src="${contextPath}/style/js/scripts.js"></script>
<script type="text/javascript">
function onComment(url){
var form = document.createElement('form');
form.action = url;
form.method = "post";
form.style.display = "none"; var input = document.createElement("input");
input.name = "title";
input.value = "${title}";
form.appendChild(input); var input2 = document.createElement("input");
input2.name = "articleId";
input2.value = "${articleId}";
form.appendChild(input2); document.body.appendChild(form);
form.submit();
}
</script>
</body>
</html>

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

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

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

 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>My-SSH-Blog</display-name> <welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list> <error-page>
<error-code>404</error-code>
<location>/public/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/public/500.html</location>
</error-page> <!-- html请求过滤器 -->
<filter>
<filter-name>articleFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>articleFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>articleFilter</filter-name>
<url-pattern>*.htm</url-pattern>
</filter-mapping> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</context-param> <!-- 监听session,在线人员信息(登录账户,不包括访客和前台在线用户) -->
<listener>
<listener-class>com.dx.ssh.listener.SessionListener</listener-class>
</listener>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- Struts2 filter -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter> <filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

ArticleFilter.java

 package com.dx.ssh.filter;

 import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils; import com.dx.ssh.service.ArticleService;
import com.dx.ssh.utils.TemplateUtils;
import com.opensymphony.xwork2.ActionContext; public class ArticleFilter implements Filter {
public static final String ARTICLE_TOKEN = "view";
private ArticleService articleService; public ArticleService getArticleService() {
return articleService;
} public void setArticleService(ArticleService articleService) {
this.articleService = articleService;
} // protected Object getBean(ServletContext servletContext, String id) {
// WebApplicationContext context =
// WebApplicationContextUtils.getWebApplicationContext(servletContext);
// return context.getBean(id);
// } @Override
public void init(FilterConfig filterConfig) throws ServletException {
} @Override
public void destroy() {
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse; // ArticleService articleService = (ArticleService)
// this.getBean(request.getServletContext(), "articleService"); String path = request.getRequestURL().toString();
// 模式匹配
Pattern pattern = Pattern.compile("/blog/([0-9]+)/([0-9]+)-([0-9]+)");
Matcher matcher = pattern.matcher(path); // 不匹配,路径不正确
if (!matcher.find()) {
// 路径不对,报错404
response.sendError(404, "您输入路径的不存在!");
return;
}
// 模版文件绝对路径
String realPath = request.getServletContext().getRealPath("/");
// 文件路径
String filePath = realPath + matcher.group() + ".ftl"; filePath = realPath + matcher.group() + ".ftl";
filePath = realPath + "blog/template/template.ftl";
filePath = filePath.replace("/", "\\");
filePath = filePath.replace("\\\\", "\\"); System.out.println(filePath);
// 查看服务器资源是否存在
File file = new File(filePath);
if (!file.exists()) {
// 路径不对,报错404
response.sendError(404, "您输入路径的不存在!");
return;
}
// 解析路径中的文章id
int articleId = Integer.parseInt(matcher.group(3));
System.out.println("articleId = " + articleId); // 防止同一用户session添加多次访问量
boolean isNew = false;
// 获取当前用户session
HttpSession session = request.getSession();
// 还没看过就能添加访问量
if (session.getAttribute(ARTICLE_TOKEN + articleId) == null) {
isNew = true;
// 设置当前的用户session已经看过文章了
session.setAttribute(ARTICLE_TOKEN + articleId, "true");
} // 填充模版信息
Map<String, Object> params = null;
try {
// 得到freemarker模版文件所需参数
params = this.getArticleService().getTemplateParams(articleId, request.getContextPath(), isNew);
params.put("contextPath", request.getContextPath());
} catch (Exception e) {
e.printStackTrace();
} String templateDir = realPath + File.separator + "blog";
String templateFile = matcher.group(1) + "/" + matcher.group(2) + "-" + matcher.group(3) + ".ftl";
templateFile = "template/template.ftl";
boolean result = TemplateUtils.parserTemplate(templateDir, templateFile, params, response.getOutputStream()); if (!result) {
// 服务器异常
response.sendError(500, "服务器未知异常!");
}
response.getOutputStream().close();
} }

TemplateUtils.java

 package com.dx.ssh.utils;

 import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Map; import freemarker.template.Configuration;
import freemarker.template.Template; public class TemplateUtils {
private static Configuration configuration = null; private TemplateUtils() {
} public static Configuration getConfiguration(String templateDir) throws IOException {
if (configuration == null) {
configuration = new Configuration();
configuration.setDirectoryForTemplateLoading(new File(templateDir));
}
return configuration;
} /**
* 向ftl模版中的数据替换
* */
public static boolean parserTemplate(String templateDir, String ftlPath, Map<String, Object> map, OutputStream os) {
try {
Template template = getConfiguration(templateDir).getTemplate(ftlPath, "UTF-8");
template.process(map, new OutputStreamWriter(os, "UTF-8"));
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

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

 /**
* 得到freemarker模版文件所需参数
*/
@Override
public Map<String, Object> getTemplateParams(int articleId, String contextPath, boolean isNew) {
System.out.println("articleId=" + articleId);
// 要看的文章
ArticleEntity article = getArticleDao().findById(articleId); if (article == null || article.getId() <= 0)
return null; // 最新三篇文章
List<ArticleEntity> lastArticles = getArticleDao().findAllLastArticle(3); // 所有类别
List<ArticleCategoryEntity> categories = getArticleCategoryDao().findAll(); // 下一篇
ArticleEntity next = null;
List<ArticleEntity> nextArticles = articleDao.findNextArticle(3, article.getCreateDate());
if (nextArticles == null || nextArticles.size() <= 0) {
next = new ArticleEntity();
next.setStaticUrl("#");
next.setTitle("这是最后一篇了哦!");
} else {
next = nextArticles.get(0);
next.setStaticUrl(contextPath + next.getStaticUrl() + ".html");
} // 上一篇文章
ArticleEntity last = null;
List<ArticleEntity> lastAs = articleDao.findLastArticle(3, article.getCreateDate());
if (lastAs == null || lastAs.size() <= 0) {
last = new ArticleEntity();
last.setStaticUrl("#");
last.setTitle("这是第一篇哦!");
} else {
last = lastAs.get(0);
last.setStaticUrl(contextPath + last.getStaticUrl() + ".html");
} java.text.SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 封装模版所需参数
Map<String, Object> params = new HashMap<String, Object>();
params.put("looked", article.getLooks());
params.put("articleId", article.getId());
params.put("time", format.format(article.getCreateDate()));
params.put("title", article.getTitle());
params.put("content", article.getContent());
params.put("typeString", "------------------------------------------------------");
params.put("author", article.getAuthor());
params.put("categoryName", getArticleCategoryDao().findById(article.getCategoryId()).getTitle());
params.put("likes", article.getLikes());
params.put("listCategoryArticle", contextPath + "/listArticle.action?categoryId=" + article.getCategoryId());
params.put("lastArticlesList", lastArticles);
params.put("categoryList", categories);
params.put("likesURL", contextPath + "/likeAction.action?articleId=" + article.getId());
params.put("nextArticle", next);
params.put("lastArticle", last);
params.put("staticURL", article.getStaticUrl()); return params;
}

FreeMarker的用法的更多相关文章

  1. Freemarker简单用法

    Freemarker 最简单的例子程序   freemarker-2.3.18.tar.gz http://cdnetworks-kr-1.dl.sourceforge.net/project/fre ...

  2. Freemarker 程序开发

    Freemarker 程序开发 现在web开发中,多使用freemarker 来描述页面.通常会使用的macro来定义各种组件,从而达到UI组件的复用.结合使用其它的指定,可快速的描述一个html页面 ...

  3. 【JavaWeb】FreeMarker快速入门

    FreeMarker Freemarker是免费开源的模板引擎技术: Freemarker脚本为Freemarker Template Language: Freemarker提供了大量内建函数来简化 ...

  4. freemark学习

    学习地址: http://blog.csdn.net/hejinxu/article/details/6694890   对freemarker的用法与语法进行了详细的讲解 http://freema ...

  5. JAVA生成Word文档(经过测试)

    首先告诉大家这篇文章的原始出处:http://www.havenliu.com/java/514.html/comment-page-1#comment-756 我也是根据他所描述完成的,但是有一些地 ...

  6. springBoot入门教程(图文+源码+sql)

    springBoot入门 1   springBoot 1.1 SpringBoot简介 Spring Boot让我们的Spring应用变的更轻量化.比如:你可以仅仅依靠一个Java类来运行一个Spr ...

  7. springboot2.0(二)

    三. Web开发 3.1.静态资源访问 在我们开发Web应用的时候,需要引用大量的js.css.图片等静态资源. 默认配置 Spring Boot默认提供静态资源目录位置需置于classpath下,目 ...

  8. 【学习】019 SpringBoot

    一.SpringBoot介绍 1.1.SpringBoot简介 在您第1次接触和学习Spring框架的时候,是否因为其繁杂的配置而退却了?在你第n次使用Spring框架的时候,是否觉得一堆反复黏贴的配 ...

  9. SpringBoot之WEB开发-专题二

    SpringBoot之WEB开发-专题二 三.Web开发 3.1.静态资源访问 在我们开发Web应用的时候,需要引用大量的js.css.图片等静态资源. 默认配置 Spring Boot默认提供静态资 ...

随机推荐

  1. Flask入门HelloWorld

    Flask入门HelloWorld Flask官网:http://flask.pocoo.org/ Flask中文翻译:http://dormousehole.readthedocs.io/en/la ...

  2. [模拟赛] T2 不等数列

    Description 将1到n任意排列,然后在排列的每两个数之间根据他们的大小关系插入">"和"<".问在所有排列中,有多少个排列恰好有k个&qu ...

  3. linux性能调试之iostat

    iostat用来监控调试linux系统的IO性能. 一般用法: iostat -xdct time_interval count [disk] -x:显示扩展统计项,如果不使用-x参数只会打印基本统计 ...

  4. 常用的Maven依赖

    一.数据库类型 1.mysql驱动 <!-- mysql驱动支持 --> <dependency> <groupId>mysql</groupId> & ...

  5. Spark Kudu 结合

    Kudu的背景 Hadoop中有很多组件,为了实现复杂的功能通常都是使用混合架构, Hbase:实现快速插入和修改,对大量的小规模查询也很迅速 HDFS/Parquet + Impala/Hive:对 ...

  6. 理解HDFS

    综述 当数据集的大小超过一台独立的物理计算机的存储能力时,就有必要对它进行分区并存储到若干台单独的计算机上.HDFS是hadoop的主要分布式存储系统,一个HDFS集群主要包括NameNode用来管理 ...

  7. DotNetCore跨平台~Json动态序列化属性

    回到目录 Json动态序列化属性,主要为了解决一个大实体,在返回前端时根据需要去序列化,如果实体里的某个属性在任务情况下都不序列化,可以添加[JsonIgnore]特性,这种是全局的过滤,但是更多的情 ...

  8. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  9. 网络1712--c语言嵌套循环作业总结

    1.助教有话说 首先,每周一篇的博客作业是很有必要的:编程的过程不仅仅是会敲几行代码.能够通过PTA就大吉大利了,你更应该做到的是梳理代码思路,通过与他人代码思路的比对,取其精华,进而不断进阶,才能逐 ...

  10. C语言博客作业—嵌套循环

    一.PTA实验作业 题目1:7-4 换硬币 1. 本题PTA提交列表 2. 设计思路 (1)定义整型变量money表示待换的零钱总额,p5表示5分硬币的数量,p2表示2分硬币的数量,p1表示1分硬币的 ...