1、介紹-FreeMarker是什麼
模板引擎:一種基於模板的、用來生成輸出文本的通用工具
基於Java的開發包和類庫
2、介紹-FreeMarker能做什麼
MVC框架中的View層組件
Html頁面靜態化
代碼生成工具
CMS模板引擎
頁面欄目動態定製
3、介紹-爲什麼要用FreeMarker
程序邏輯(Java 程序)和頁面設計(FreeMarker模板)分離
分層清晰,利於分工合作
主流Web框架良好的集成(struts2,springmvc)
簡單易學、功能強大
免費開源
4、FreeMarker優點
FreeMarker不依賴於Servlet,網絡或Web 環境
FreeMarker一開始就是爲MVC設計的,它僅僅專注於展示
你可以從任意位置加載模板;從類路徑下,從數據庫中等
易於定義特設的宏和函數
5、上面簡單介紹一下Freemarker,下面主要是利用Freemarker實習網頁靜態化的功能。
通過上面的介紹知道Freemarker是一種基於模板的、用來生成輸出文本的通用工具,所以我們必須要定製符合自己業務的模板出來,然後生成的我們得html頁面
Freemarker是通過freemarker.template.Configuration這個對象對模板進行加載的(它也處理創建和緩存預解析模板的工作),然後我們通過getTemplate方法獲得你想要的模板,有一點要記住freemarker.template.Configuration在你整個應用必須保證唯一實例。
5.1、在Configuration 中可以使用下面的方法來方便建立三種模板加載
-
void setDirectoryForTemplateLoading(File dir);
-
void setClassForTemplateLoading(Class cl, String prefix);
-
void setServletContextForTemplateLoading(Object servletContext, String path);
上述的第一種方法在磁盤的文件系統上設置了一個明確的目錄,它確定了從哪裏加載模板。不要說可能,File 參數肯定是一個存在的目錄。否則,將會拋出異常。
第二種調用方法使用了一個Class 類型的參數和一個前綴。這是讓你來指定什麼時候通過相同的機制來加載模板,不過是用Java 的ClassLoader 來加載類。這就意味着傳
入的Class 參數會被用來調用Class.getResource()方法來找到模板。參數prefix是給模板的名稱來加前綴的。在實際運行的環境中,類加載機制是首選用來加載模板的方法,因爲通常情況下,從類路徑下加載文件的這種機制,要比從文件系統的特定目錄位置加載安全而且簡單。在最終的應用程序中,所有代碼都使用.jar 文件打包也是不錯的,這樣用戶就可以直接執行包含所有資源的.jar
文件了。
第三種調用方式需要Web 應用的上下文和一個基路徑作爲參數,這個基路徑是Web 應用根路徑(WEB-INF 目錄的上級目錄)的相對路徑。那麼加載器將會從Web 應用目錄開
始加載模板。儘管加載方法對沒有打包的.war 文件起作用, 因爲它使用了ServletContext.getResource()方法來訪問模板,注意這裏我們指的是“目錄”。如果忽略了第二個參數(或使用了””),那麼就可以混合存儲靜態文件(.html,.jpg 等)和.ftl 文件,只是.ftl 文件可以被送到客戶端執行。當然必須在WEB-INF/web.xml中配置一個Servlet
來處理URI 格式爲*.ftl 的用戶請求,否則客戶端無法獲取到模板,因此你將會看到Web 服務器給出的祕密提示內容。在站點中不能使用空路徑,這將成爲一個問題,你應該在WEB-INF 目錄下的某個位置存儲模板文件,這樣模板源文件就不會偶然
void setDirectoryForTemplateLoading(File dir);
void setClassForTemplateLoading(Class cl, String prefix);
void setServletContextForTemplateLoading(Object
servletContext, String path);
地被執行到,這種機制對servlet 應用程序來加載模板來說,是非常好用的方式,而且模板可以自動更新而不需重啓Web 應用程序,但是對於類加載機制,這樣就行不通了。
5.2、從多個位置加載模板
-
import freemarker.cache.*;
-
...
-
FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates"));
-
FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates"));
-
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(),"");
-
TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2,ctl };
-
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);
-
cfg.setTemplateLoader(mtl);
現在,FreeMarker 將會嘗試從/tmp/templates 目錄加載模板,如果在這個目錄下沒有發現請求的模板,它就會繼續嘗試從/usr/data/templates 目錄下加載,如果還是沒有發現請求的模板,那麼它就會使用類加載器來加載模板。
5.3、封裝freemarker用於創建模板和加載模板
-
package com.ajun.template.utils;
-
-
import java.io.IOException;
-
import java.io.Writer;
-
import java.util.Locale;
-
import java.util.Map;
-
-
import javax.servlet.ServletContext;
-
-
import freemarker.template.Configuration;
-
import freemarker.template.DefaultObjectWrapper;
-
import freemarker.template.Template;
-
import freemarker.template.TemplateException;
-
-
-
-
-
-
public class FreeMarkertUtil {
-
-
private static Configuration config = new Configuration();
-
-
-
-
-
-
-
public static void processTemplate(String templateName, Map<?,?> root, Writer out){
-
try{
-
-
Template template=config.getTemplate(templateName,"utf-8");
-
-
template.process(root, out);
-
out.flush();
-
} catch (IOException e) {
-
e.printStackTrace();
-
} catch (TemplateException e) {
-
e.printStackTrace();
-
}finally{
-
try {
-
out.close();
-
out=null;
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
-
-
-
-
-
public static void initConfig(ServletContext servletContext,String templateDir){
-
config.setLocale(Locale.CHINA);
-
config.setDefaultEncoding("utf-8");
-
config.setEncoding(Locale.CHINA, "utf-8");
-
config.setServletContextForTemplateLoading(servletContext, templateDir);
-
config.setObjectWrapper(new DefaultObjectWrapper());
-
}
-
}
5.4、例子介紹
會用freemarker.jar自己google下載吧。
這個例子中我們會Freemarker生成一個html文件 包括html的頭部和尾部,已經body,這三個部分會分別對應三個模板文件,如下:
在模板內要想輸出結果集 可以用類似於EL表達式輸出${}
header.ftl
-
companyName==>${h.companyName}<br/>
-
address==>${h.address}<br/>
footer.ftl
-
des==>${f.des}<br/>
-
-
<a href="http://localhost/htmlpage/UpdateFooter.do"> 更新Footer </a>
body.ftl,這個模板include以上兩個模板文件
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<html>
-
<head>
-
<title>用戶列表</title>
-
-
<meta http-equiv="pragma" content="no-cache">
-
<meta http-equiv="cache-control" content="no-cache">
-
<meta http-equiv="expires" content="0">
-
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
-
<meta http-equiv="description" content="This is my page">
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
<!--
-
<link rel="stylesheet" type="text/css" href="styles.css">
-
-->
-
-
</head>
-
-
<body>
-
<#include "header.ftl" parse=true encoding="utf-8">
-
<hr/>
-
<a href="#">用戶列表</a><br/>
-
<table border="1">
-
<tr>
-
<td>用戶名</td>
-
<td>年齡</td>
-
<td>生日</td>
-
<td>id</td>
-
<td>操作</td>
-
</tr>
-
<#list users as user>
-
<tr>
-
<td>${user.name}</td>
-
<td>${user.age}</td>
-
<td>
-
${user.birthday?string("yyyy-MM-dd HH:mm:ss")}
-
</td>
-
<td>${user.id}</td>
-
<td><a href="http://localhost/htmlpage/DelUser.do?id=${user.id}">刪除</a></td>
-
</tr>
-
</#list>
-
-
</table>
-
<hr/>
-
<#include "footer.ftl" parse=true encoding="utf-8">
-
</body>
-
</html>
這三個模板對應的三個實體類
Footer.java
-
package com.ajun.template.bean;
-
-
-
-
-
-
public class Footer {
-
-
private String des;
-
-
public String getDes() {
-
return des;
-
}
-
-
public void setDes(String des) {
-
this.des = des;
-
}
-
-
-
}
Header.java
-
package com.ajun.template.bean;
-
-
-
-
-
public class Header {
-
-
private String companyName;
-
-
private String address;
-
-
public String getCompanyName() {
-
return companyName;
-
}
-
-
public void setCompanyName(String companyName) {
-
this.companyName = companyName;
-
}
-
-
public String getAddress() {
-
return address;
-
}
-
-
public void setAddress(String address) {
-
this.address = address;
-
}
-
-
-
-
}
User.java
-
package com.ajun.template.bean;
-
-
import java.util.Date;
-
-
public class User {
-
-
private Integer id;
-
-
private String name ;
-
-
private int age;
-
-
private Date birthday;
-
-
public String getName() {
-
return name;
-
}
-
-
public void setName(String name) {
-
this.name = name;
-
}
-
-
public int getAge() {
-
return age;
-
}
-
-
public void setAge(int age) {
-
this.age = age;
-
}
-
-
public Date getBirthday() {
-
return birthday;
-
}
-
-
public void setBirthday(Date birthday) {
-
this.birthday = birthday;
-
}
-
-
-
public Integer getId() {
-
return id;
-
}
-
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
-
public User(Integer id,String name, int age, Date birthday) {
-
super();
-
this.name = name;
-
this.age = age;
-
this.birthday = birthday;
-
this.id = id;
-
}
-
-
public User() {
-
super();
-
}
-
-
-
}
下面模板一些業務邏輯操作,對這三個實體類
-
package com.ajun.template.service;
-
-
import com.ajun.template.bean.Footer;
-
-
-
-
-
public class FooterService {
-
-
private static Footer f = new Footer();
-
static{
-
f.setDes("北京-廊坊-好公司呢!!!!哇哈哈!!!");
-
}
-
-
public static void update(String des){
-
f.setDes(des);
-
}
-
-
public static Footer gerFooter(){
-
return f;
-
}
-
}
-
package com.ajun.template.service;
-
-
import com.ajun.template.bean.Header;
-
-
-
-
-
public class HeaderService {
-
-
private static Header h = new Header();
-
-
static{
-
h.setAddress("北京朝陽CBD");
-
h.setCompanyName("上海唐秀!!!");
-
}
-
-
public static void update(String address,String companyName){
-
h.setAddress(address);
-
h.setCompanyName(companyName);
-
}
-
-
public static Header getHeader(){
-
return h;
-
}
-
}
-
package com.ajun.template.service;
-
-
import java.util.ArrayList;
-
import java.util.Date;
-
import java.util.List;
-
-
import com.ajun.template.bean.User;
-
-
-
-
-
public class UserService {
-
-
private static List<User> users = new ArrayList<User>();
-
-
static{
-
for(int i=0;i<10;i++){
-
User u = new User(i,"ajun"+i,i+10,new Date());
-
users.add(u);
-
}
-
}
-
-
public static List<User> getUsers(){
-
return users;
-
}
-
-
public static void delete(int index){
-
for(int i=0 ;i<users.size();i++){
-
User u = users.get(i);
-
if(u.getId()==index){
-
users.remove(u);
-
-
}
-
}
-
}
-
}
上面主要是模板你的一些業務和dao層得操作,因此沒有涉及數據庫的操作,主要是爲實驗。
生成html對外調用的方法,會用到FreeMarkertUtil這個類 這個類得代碼上面已經給出。
-
package com.ajun.template.client;
-
-
import java.io.Writer;
-
import java.util.HashMap;
-
import java.util.List;
-
import java.util.Map;
-
-
import com.ajun.template.bean.Footer;
-
import com.ajun.template.bean.Header;
-
import com.ajun.template.bean.User;
-
import com.ajun.template.service.FooterService;
-
import com.ajun.template.service.HeaderService;
-
import com.ajun.template.service.UserService;
-
import com.ajun.template.utils.FreeMarkertUtil;
-
-
-
-
-
-
public class ProcessClient {
-
-
private static Map<String,Object> root = new HashMap<String,Object>();
-
-
-
-
-
-
-
-
public static void processBody(Writer out){
-
Header h = HeaderService.getHeader();
-
root.put("h", h);
-
Footer f = FooterService.gerFooter();
-
root.put("f", f);
-
List<User> users = UserService.getUsers();
-
root.put("users", users);
-
FreeMarkertUtil.processTemplate("body.ftl", root, out);
-
}
-
-
}
此時我會提供一個servlet在客戶端進行第一次請求的時候 我會調用這個ProcessClient來生成html頁面,之後每次訪問就可以直接訪問html,來做到真正的靜態化了
-
package com.ajun.template.servlet;
-
-
import java.io.File;
-
import java.io.FileOutputStream;
-
import java.io.IOException;
-
import java.io.OutputStreamWriter;
-
import java.io.Writer;
-
-
import javax.servlet.ServletConfig;
-
import javax.servlet.ServletException;
-
import javax.servlet.http.HttpServlet;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
-
import com.ajun.template.client.ProcessClient;
-
import com.ajun.template.utils.DirectoryFilter;
-
import com.ajun.template.utils.FreeMarkertUtil;
-
-
-
-
-
-
public class Index extends HttpServlet {
-
-
private static final long serialVersionUID = 7474850489594438527L;
-
-
public Index() {
-
super();
-
}
-
-
-
public void doGet(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
-
this.doPost(request, response);
-
}
-
-
-
public void doPost(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
-
String dirPath = request.getSession().getServletContext().getRealPath("/templates/html");
-
File path = new File(dirPath);
-
-
String indexFileName = "index.html";
-
-
-
-
String[] indexfileList = path.list(new DirectoryFilter(indexFileName));
-
if(indexfileList.length<=0){
-
Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+indexFileName),"UTF-8");
-
-
ProcessClient.processBody(out);
-
request.getRequestDispatcher("/templates/html/index.html").forward(request, response);
-
}else{
-
request.getRequestDispatcher("/templates/html/"+indexfileList[0]).forward(request, response);
-
}
-
-
-
}
-
-
-
-
-
-
-
public void init(ServletConfig config) throws ServletException {
-
String templateDir = config.getInitParameter("templateDir");
-
FreeMarkertUtil.initConfig(config.getServletContext(), templateDir);
-
}
-
-
-
}
web.xml配置
-
<servlet>
-
<description>This is the description of my J2EE component</description>
-
<display-name>This is the display name of my J2EE component</display-name>
-
<servlet-name>Index</servlet-name>
-
<servlet-class>com.ajun.template.servlet.Index</servlet-class>
-
<init-param>
-
<param-name>templateDir</param-name>模板存放位置,是基於app的根目錄的
-
<param-value>/templates</param-value>
-
</init-param>
-
<load-on-startup>3</load-on-startup>爲了啓動的時候初始化模板配置
-
</servlet>
-
-
<servlet-mapping>
-
<servlet-name>Index</servlet-name>
-
<url-pattern>/Index.do</url-pattern>
-
</servlet-mapping>
部署到tomcat上,輸入:http://localhost/htmlpage/Index.do
頁面效果:
頁面是做好了,但是內容變化了 ,更新怎麼辦呢,我這裏是當列表內容變化之後 ,我是刪除原來的html ,利用模板然後重新生成的符合新結果的html頁面
當我刪除一條的時候,我會重新生成html頁面,但是由於瀏覽器緩存的原因,即是你刪除了,重新生成了新html頁面,可是瀏覽器還是保存原來的頁面,不刷新兩次是不行的,
這裏我採用的沒更新的時候,都會給這個html改個名字,讓瀏覽器去加載最新的,就可以了
具體的刪除操作如下:
-
package com.ajun.template.servlet;
-
-
import java.io.File;
-
import java.io.FileOutputStream;
-
import java.io.IOException;
-
import java.io.OutputStreamWriter;
-
import java.io.Writer;
-
import java.util.UUID;
-
-
import javax.servlet.ServletException;
-
import javax.servlet.http.HttpServlet;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
-
import com.ajun.template.client.ProcessClient;
-
import com.ajun.template.service.UserService;
-
import com.ajun.template.utils.DirectoryFilter;
-
-
-
-
-
public class DelUser extends HttpServlet {
-
-
-
public void doGet(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
this.doPost(request, response);
-
}
-
-
-
public void doPost(HttpServletRequest request, HttpServletResponse response)
-
throws ServletException, IOException {
-
-
String id = request.getParameter("id");
-
UserService.delete(Integer.valueOf(id));
-
-
-
String dirPath = request.getSession().getServletContext().getRealPath("/templates/html");
-
-
String indexFileName = "index.html";
-
-
-
delOldHtml(dirPath,indexFileName);
-
-
-
UUID uuid = UUID.randomUUID();
-
Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+uuid+indexFileName),"UTF-8");
-
ProcessClient.processBody(out);
-
response.sendRedirect("templates/html/"+uuid+"index.html");
-
}
-
-
-
-
-
-
-
private void delOldHtml(String htmlDir,String htmlName){
-
File path = new File(htmlDir);
-
String[] indexfileList = path.list(new DirectoryFilter(htmlName));
-
if(indexfileList.length>=0){
-
for(String f:indexfileList){
-
File delf = new File(htmlDir+"/"+f);
-
delf.delete();
-
}
-
}
-
}
-
-
}
通過以上操作,每次更新html,就可以不解決瀏覽器緩存的問題了。
還有一個工具類需要介紹,就是判斷是否已經生成了特定的html文件的java類
-
package com.ajun.template.utils;
-
-
import java.io.File;
-
import java.io.FilenameFilter;
-
-
-
-
-
public class DirectoryFilter implements FilenameFilter {
-
-
String myString;
-
public DirectoryFilter(String myString)
-
{
-
this.myString = myString;
-
}
-
-
public boolean accept(File dir,String name)
-
{
-
-
String f= new File(name).getName();
-
if(f.contains(myString) || f.equals(myString)){
-
return true;
-
}
-
return false;
-
}
-
-
}
到這裏整個靜態化就完成了,靜態化更新機制,是根據你自己項目的業務進行定製的,可以定時生成html文件,也可以需要手動生成。
項目結構圖如下:
記住:網站不是所有的頁面都是需要靜態化的,主要是一些實時性不是很高的數據頁面進行靜態化(來提高訪問速度),其他都是通過僞靜態來實現的,就是重寫utl。
頁面靜態化不是提高網站性能的唯一途徑,還可以利用一些緩存產品來實現。
常用FreeMarker資源
官網主頁:http://www.freemarker.org/
Eclipse插件JbossTool:http://www.jboss.org/tools/download/
中文文檔:https://sourceforge.net/projects/freemarker/files/chinese-manual/FreeMarker_Manual_zh_CN.pdf/download