目录
一、FreeMarker简介
二、第一个FreeMark示例
2.1、新建一个Maven项目
2.2、添加依赖
2.3、添加存放模板的文件夹
2.4、添加模板
2.5、解析模板
2.6、运行结果
三、动态页面静态化
3.1、动态页面静态化的优点
3.2、动态页面静态化的问题
3.3、实现动态页面静态化
3.3.1、新建一个基于Maven的Web项目
3.3.2、添加依赖
3.3.3、创建文章POJO类
3.3.4、创建文章业务类
3.3.5、添加模板
3.3.6、添加Servlet生成静态页
3.3.7、运行结果
四、FreeMarker模板语法
4.0、模板文件的4个组成部分
4.1、FTL指令规则
4.2、 插值规则
4.3、表达式
4.3.1、直接指定值
4.3.2、输出变量值
4.3.3、字符串操作
4.3.4、集合连接运算符
4.3.5、Map连接运算符
4.3.6、算术运算符
4.3.7、比较运算符
4.3.8、逻辑运算符
4.3.9、内建函数
4.3.10、空值处理运算符
4.3.11、运算符的优先级
4.4、FreeMarker的常用指令
4.4.1、if指令
4.4.2、switch , case , default , break指令
4.4.3、list, break指令
4.4.4、include指令
4.4.5、import指令
4.4.6、noparse指令
4.4.7、escape , noescape指令
4.4.8、assign指令
4.4.9、setting指令
4.4.10、macro , nested , return指令
五、示例下载
模板引擎可以让程序实现界面与数据分离,业务代码与逻辑代码的分离,这就提升了开发效率,良好的设计也使得代码复用变得更加容易。一般的模板引擎都包含一个模板解析器和一套标记语言,好的模板引擎有简洁的语法规则、强大的功能、高效的渲染效率、详尽的帮助说明与不断的更新与维护。常见的前端模板引擎有:
常用的java后台模板引擎:jsp、FreeMarker、Velocity等。
请不要迷恋速度,为了推广的测试可能是片面的,好的模板引擎经得起时间考验,建议大家选择成熟的、常用的模板引擎。另外不管前后端的模板引擎原理都差不多,不外乎在模板中定义一些特别的标签后台正则匹配后替换,所以这里就以FreeMarker为例简介一下模板引擎的使用。另外我个人觉得ASP.NET MVC中使用的razor模板引擎非常好用,如果java有一款类似的就好了。
一、FreeMarker简介
FreeMarker是一款模板引擎,即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarkerTemplateLanguage(FTL)。它是简单的,专用的语言,不是像PHP那样成熟的编程语言。那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。
这种方式通常被称为MVC(模型视图控制器)模式,对于动态网页来说,是一种特别流行的模式。它帮助从开发人员(Java程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑,在没有程序员来修改或重新编译代码时,也可以修改页面的样式。
而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
特征与亮点:
功能强大的模板语言:有条件的块,迭代,赋值,字符串和算术运算和格式化,宏和函数,编码等更多的功能;
多用途且轻量:零依赖,输出任何格式,可以从任何地方加载模板(可插拔),配置选项丰富;
智能的国际化和本地化:对区域设置和日期/时间格式敏感。
XML处理功能:将dom-s放入到XML数据模型并遍历它们,甚至处理他们的声明
通用的数据模型:通过可插拔适配器将java对象暴露于模板作为变量树。
FreeMarker是免费的,基于Apache许可证2.0版本发布。
获得FreeMarker
中文帮助文档:https://sourceforge.net/projects/freemarker/files/chinese-manual/
下载FreeMarker jar包:下载地址http://freemarker.org/freemarkerdownload.html
使用Maven依赖jar包:
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker-gae</artifactId> <version>2.3.25-incubating</version> </dependency>
部分下载包卡住的 记得及时删除还不行请换version 版本号
FreeMarker用途
生成HTML Web页面,如作为MVC框架的视图
动态页面静态化等
代码生成器
二、第一个FreeMark示例
模板 + 数据模型 = 输出,FreeMarker基于设计者和程序员是具有不同专业技能的不同个体的观念,他们是分工劳动的:设计者专注于表示——创建HTML文件、图片、Web页面的其它可视化方面;程序员创建系统,生成设计页面要显示的数据。总之实现了数据与表现的分离。
2.1、新建一个Maven项目
新建一个简单Maven项目,不要选择内置模板,设置jdk版本为1.7。
2.2、添加依赖
修改pom.xml配置文件,增加freemark、junit依赖,修改后的pom.xml文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>FreeMarkerDemo</artifactId> <version>0.0.1</version> <dependencies> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> </project>
2.3、添加存放模板的文件夹
在src/main/java的包下添加一个名为“templates”目录(包),用于存放所有的freemarker模板。
2.4、添加模板
在src/main/java/templates目录下添加名为“product.ftl”的FreeMarker模板,模板的内容如下:
----------产品详细----------
产品名称:${name}
产品价格:${price}
设计作者:<#list users as user> ${user} </#list>
------------------------------
模板中一般分为不可变部分与可变部分,如“产品名称:”这些常量内容就是不可以变化的,而${}与<#></#>这些内容是可以根据数据动态变化的。
2.5、解析模板
使用FreeMarker可以读取到模板内容,将数据与模板绑定并渲染出结果,很好的实现了表现与数据分离。新建一个测试类,代码如下:
package com.zhangguo.freemarkerdemo; import java.io.File; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; public class Test { public static void main(String[] args) throws Exception { //创建一个freemarker.template.Configuration实例,它是存储 FreeMarker 应用级设置的核心部分 //指定版本号 Configuration cfg=new Configuration(Configuration.VERSION_2_3_22); //设置模板目录 cfg.setDirectoryForTemplateLoading(new File("src/main/java/templates")); //设置默认编码格式 cfg.setDefaultEncoding("UTF-8"); //数据 Map<String, Object> product = new HashMap<>(); product.put("name", "Huwei P8"); product.put("price", "3985.7"); product.put("users", new String[]{"Tom","Jack","Rose"}); //从设置的目录中获得模板 Template temp = cfg.getTemplate("product.ftl"); //合并模板和数据模型 Writer out = new OutputStreamWriter(System.out); temp.process(product, out); //关闭 out.flush(); out.close(); } }
2.6、运行结果
三、动态页面静态化
动态页面静态化是指使用服务器后台技术将用户原来请求的动态页面变成静态内容缓存于服务器文件中,比如网站有一篇新闻名为由hot.jsp页面展示,默认情况下每当有客户端从服务器请求该新闻时服务器会解析hot.jsp页面渲染出静态内容响应给客户端,这样有一些问题,首先是每次请求都要解析服务器压力大,其次新闻的内容并没有真的存储在hot.jsp文件中而是存储在数据库里,对搜索引擎的爬虫不友好,不便SEO,另外访问动态的内容可能存在安全风险,如sql注入,XSS等网络攻击。解决办法是将hot.jsp页面静态化成一个叫hot.html的文件,服务器不再执行动态内容直接把静态页面响应给客户端,因为是纯静态的服务器压力会减轻,不担心网络安全问题;文章的内容直接存储在html文件中,对SEO友好。
3.1、动态页面静态化的优点
a) 利于搜索引擎优化(SEO)
b) 减轻服务器压力
c) 提高了网站的安全性
d) 加快了客户端的访问速度
3.2、动态页面静态化的问题
a) 页面过期与内容更新问题
如果一个页面的内容需要经常更新,就会要不断的生成新的静态页面,不是所有的页面都适合静态化。
b) 页面生成的问题
什么时候生成静态页面合适,有客户端第一次请求生成的,有定时生成的,也有后台管理时批量生成的。
c) 页面中部分内容是静态的,部分内容是动态的,如一篇文章的评论,访问次数,这些肯定不能静态。我暂时想到的办法是ajax和内嵌框架(iframe)
3.3、实现动态页面静态化
实现动态页面静态化的办法多种多样,这里使用FreeMarker,仅仅提供思路,代码没有封装与优化。
3.3.1、新建一个基于Maven的Web项目
3.3.2、添加依赖
这里没有使用MVC,只需依赖FreeMarker、Servlet与JSP核心包就可以了,修改后的pom.xml文件如下。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>SpringMVC71</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!-- FreeMarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker-gae</artifactId> <version>2.3.25-incubating</version> </dependency> <!-- Servlet核心包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <!--JSP --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> </dependencies> </project>
依赖成功的结果:
3.3.3、创建文章POJO类
在src/main/java源代码目录下创建Article.java文件,该类代表文章,代码如下:
package com.zhangguo.springmvc71.entities; /** * 文章 * */ public class Article { /* * 编号 */ private int id; /* * 标题 */ private String title; /* * 内容 */ private String content; public Article() { } public Article(int id, String title, String content) { super(); this.id = id; this.title = title; this.content = content; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Article [id=" + id + ", title=" + title + ", content=" + content + "]"; } }
3.3.4、创建文章业务类
在src/main/java源代码目录下创建ArticleService.java文件,该类代表文章业务,主要提供文章数据,定义了一个文章集合中,初始化时向集合中随意添加了5个文章对象,代码如下:
package com.zhangguo.springmvc71.Services; import java.util.ArrayList; import java.util.List; import com.zhangguo.springmvc71.entities.Article; /** * 文章业务类(模拟) * */ public class ArticleService { private static List<Article> articles; static { articles = new ArrayList<Article>(); articles.add(new Article(20160701, "不明真相的美国人被UFO惊呆了 其实是长征7号","据美国《洛杉矶时报》报道,当地时间周三晚(北京时间周四),在美国中西部的犹他州、内华达州、加利福利亚州,数千人被划过夜空的神秘火球吓到")); articles.add(new Article(20160702, "法国巴黎圣母院为教堂恐袭案遇害神父举行大弥撒", "而据美国战略司令部证实,其实这是中国长征七号火箭重新进入大气层,刚好经过加利福利亚附近。")); articles.add(new Article(20160703, "日东京知事候选人小池百合子回击石原:浓妆可以", "然而昨晚的美国人民可不明真相,有些人甚至怀疑这些火球是飞机解体,还有些人猜测是流星雨。")); articles.add(new Article(20160704, "日资慰安妇基金在首尔成立 韩国示威者闯入抗议","美国战略司令部发言人表示,到目前为止还没有任何受损报告,他说类似物体通常在大气中就会消失,这也解释了为何出现一道道光痕,这一切都并未造成什么威胁。")); articles.add(new Article(20160705, "中日关系正处十字路口日应寻求减少与华冲突","中国长征七号火箭6月25日在海南文昌航天发射中心首次发射,并成功升空进入轨道。有学者指出长征七号第二级火箭一直在地球低轨运行,一个月后重新进入大气层。")); } /** * 所有的文章 */ public List<Article> getArticles() { return articles; } /* * 获得文章通过文章编号 */ public Article getArticle(int id) { for (Article article : articles) { if (article.getId() == id) { return article; } } return null; } }
3.3.5、添加模板
在src/main/java源代码目录的templates包下添加两个模板,一个名为newsList.ftl用于生成新闻列表,另一个名为news.ftl用于生成单篇新闻,newsList.ftl文件内容如下:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>新闻焦点</title> </head> <body> <div id="container"> <h2>新闻焦点</h2> <#setting number_format="#"> <ul> <#list articles as article> <li> <a href="news/${article.id}.html">${article.title}</a> </li> </#list> </ul> </div> <style> #container{ font-family:"microsoft yahei"; width:800px; margin:0 auto; } a{ color:#333; text-decoration:none; } li{ height:26px; line-height:26px; } </style> </body> </html>
文件中使用了FreeMarker标记,具体语法可以看第四点;news.ftl文件内容如下:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>${article.title}</title> </head> <body> <div id="container"> <h2>${article.title}</h2> <p> ${article.content} </p> </div> <style> #container{ font-family:"microsoft yahei"; width:800px; margin:0 auto; } </style> </body> </html>
3.3.6、添加Servlet生成静态页
新增一个名为News的Servlet类,当Servlet收到客户端请求时会查看系统中是否存在index.html(新闻列表)静态页面,如果存在直接转发,如果不存在则生成新闻列表静态页面及子页面。创建好的Servlet代码如下所示:
package com.zhangguo.springmvc71.actions; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zhangguo.springmvc71.Services.ArticleService; import com.zhangguo.springmvc71.entities.Article; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; /** *新闻列表 */ @WebServlet("/News") public class News extends HttpServlet { private static final long serialVersionUID = 1L; ArticleService articleService=new ArticleService(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ //设置编码格式与MIME类型 response.setContentType("text/html; charset=UTF-8"); //首页新闻列表路径 String indexPath=request.getServletContext().getRealPath("/index.html"); //文件是否存在 File file=new File(indexPath); if(!file.exists()){ //如果新闻列表不存在,生成新闻列表 //创建一个freemarker.template.Configuration实例,它是存储 FreeMarker 应用级设置的核心部分 //指定版本号 Configuration cfg=new Configuration(Configuration.VERSION_2_3_22); //获得模板文件路径 String templatePath=this.getClass().getClassLoader().getResource("/templates").getPath(); //设置模板目录 cfg.setDirectoryForTemplateLoading(new File(templatePath)); //设置默认编码格式 cfg.setDefaultEncoding("UTF-8"); //数据 Map<String, Object> articleData = new HashMap<>(); List<Article> articles=articleService.getArticles(); articleData.put("articles", articles); //从设置的目录中获得模板 Template template = cfg.getTemplate("newsList.ftl"); //合并模板和数据模型 try { //将数据与模板渲染的结果写入文件中 Writer writer=new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); template.process(articleData, writer); writer.flush(); articleData.clear(); template = cfg.getTemplate("news.ftl"); //生成单个新闻文件 for (Article article : articles) { articleData.put("article", article); //单个新闻文件 file=new File(request.getServletContext().getRealPath("/news/"+article.getId()+".html")); //文件输出流写入器 writer=new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); //将模板+数据生成的结果写入文件中,得到一个静态文件 template.process(articleData, writer); writer.flush(); } writer.close(); } catch (TemplateException e) { e.printStackTrace(); } } //如果新闻单页下存在,生成新闻单页 request.getRequestDispatcher("index.html").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
从代码中可以看出生成的单篇文章全部存放在news目录下,要记得在webapp根目录下创建news目录。这里只是示例代码,如果要在项目中应用,应该把FreeMarker,文件操作的内容分Servlet分开。另外web.xml文件中添加index.html为第1个欢迎页,这样做的目的是当首页被生成时直接让服务器响应index.html。web.xml文件如下:
<?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"> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
index.jsp直接转发到News Servlet中,文件内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:forward page="News"></jsp:forward>
3.3.7、运行结果
3.3.8、小结
再次强调这只是一个示例;另外你可能会想到FreeMarker在这里的作用感觉不大,如果我们使用一些特别的标记嵌套在静态的页面中,然后后台替换其实同样可以做到,确实这样也可以,但对于复杂的内容FreeMarker替换的方式要更加优雅,效率要更高,如果你使用jsp作为模板效果也是一样的,只是他们的侧重点不一样而已,有点想念razor了。
由于篇幅太长 决定将 四、FreeMarker模板语法 比较复杂的模版需要FreeMarker语法规则 决定在【FreeMarker】中写
五、示例下载
http://files.cnblogs.com/files/best/SpringMVC71.rar
- 一、FreeMarker简介
- 二、第一个FreeMark示例
- 三、动态页面静态化
- 四、FreeMarker模板语法
- 4.0、模板文件的4个组成部分
- 4.1、FTL指令规则
- 4.2、 插值规则
- 4.3、表达式
- 4.3.1、直接指定值
- 4.3.2、输出变量值
- 4.3.3、字符串操作
- 4.3.4、集合连接运算符
- 4.3.5、Map连接运算符
- 4.3.6、算术运算符
- 4.3.7、比较运算符
- 4.3.8、逻辑运算符
- 4.3.9、内建函数
- 4.3.10、空值处理运算符
- 4.3.11、运算符的优先级
- 4.4、FreeMarker的常用指令
- 五、示例下载