一、介紹-FreeMarker是什麼css
模板引擎:一種基於模板的、用來生成輸出文本的通用工具html
基於Java的開發包和類庫java
二、介紹-FreeMarker能作什麼web
MVC框架中的View層組件spring
Html頁面靜態化數據庫
代碼生成工具瀏覽器
CMS模板引擎緩存
頁面欄目動態定製tomcat
三、介紹-爲何要用FreeMarker安全
程序邏輯(Java 程序)和頁面設計(FreeMarker模板)分離
分層清晰,利於分工合做
主流Web框架良好的集成(struts2,springmvc)
簡單易學、功能強大
免費開源
四、FreeMarker優勢
FreeMarker不依賴於Servlet,網絡或Web 環境
FreeMarker一開始就是爲MVC設計的,它僅僅專一於展現
你能夠從任意位置加載模板;從類路徑下,從數據庫中等
易於定義特設的宏和函數
五、上面簡單介紹一下Freemarker,下面主要是利用Freemarker實習網頁靜態化的功能。
經過上面的介紹知道Freemarker是一種基於模板的、用來生成輸出文本的通用工具,因此咱們必需要定製符合本身業務的模板出來,而後生成的咱們得html頁面
Freemarker是經過freemarker.template.Configuration這個對象對模板進行加載的(它也處理建立和緩存預解析模板的工做),而後咱們經過getTemplate方法得到你想要的模板,有一點要記住freemarker.template.Configuration在你整個應用必須保證惟一實例。
5.一、在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 目錄下的某個位置存儲模板文件,這樣模板源文件就不會偶然地被執行到,這種機制對servlet 應用程序來加載模板來講,是很是好用的方式,並且模板能夠自動更新而不需重啓Web 應用程序,可是對於類加載機制,這樣就行不通了。
5.二、從多個位置加載模板
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.三、封裝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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ public class FreeMarkertUtil { private static Configuration config = new Configuration(); /** * @param templateName 模板名字 * @param root 模板根 用於在模板內輸出結果集 * @param out 輸出對象 具體輸出到哪裏 */ public static void processTemplate(String templateName, Map<?,?> root, Writer out){ try{ //得到模板 Template template=config.getTemplate(templateName,"utf-8"); //生成文件(這裏是咱們是生成html) 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(); } } } /** * 初始化模板配置 * @param servletContext javax.servlet.ServletContext * @param templateDir 模板位置 */ 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.四、例子介紹
會用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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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); //users.remove(index); } } } }
上面主要是模板你的一些業務和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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ public class ProcessClient { private static Map<String,Object> root = new HashMap<String,Object>(); /** * 調用FreeMarkertUtil.java * FreeMarkertUtil.processTemplate("body.ftl", root, out); * 來生成html文件 * @param out */ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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 { //html生成以後存放的路徑 String dirPath = request.getSession().getServletContext().getRealPath("/templates/html"); File path = new File(dirPath); //生成的文件的名字 String indexFileName = "index.html"; /** * 判斷是否已經存在該html文件,存在了就直接訪問html ,不存在生成html文件 */ String[] indexfileList = path.list(new DirectoryFilter(indexFileName)); if(indexfileList.length<=0){ Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+indexFileName),"UTF-8"); //生成html文件 ProcessClient.processBody(out); request.getRequestDispatcher("/templates/html/index.html").forward(request, response); }else{ request.getRequestDispatcher("/templates/html/"+indexfileList[0]).forward(request, response); } } /** * 初始化模板配置,供之後得到模板,在init里加載也主要是爲保證Configuration實例惟一 */ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ 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)); //生成html的位置 String dirPath = request.getSession().getServletContext().getRealPath("/templates/html"); //文件名字 String indexFileName = "index.html"; //刪除原來的文件 delOldHtml(dirPath,indexFileName); //防止瀏覽器緩存,用於從新生成新的html 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"); } /** * 刪除原來的html文件 * @param htmlDir * @param htmlName */ 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; /** * @author ajun * @http://blog.csdn.net/ajun_studio **/ public class DirectoryFilter implements FilenameFilter { String myString; public DirectoryFilter(String myString) { this.myString = myString; } public boolean accept(File dir,String name) { //FilenameFilter.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/
本文轉自:http://blog.csdn.net/ajun_studio/article/details/6932185