一、介紹-FreeMarker是什麼 模板引擎:一種基於模板的、用來生成輸出文本的通用工具 基於Java的開發包和類庫 二、介紹-FreeMarker能作什麼 MVC框架中的View層組件 Html頁面靜態化 代碼生成工具 CMS模板引擎 頁面欄目動態定製 三、介紹-爲何要用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 中可使用下面的方法來方便創建三種模板加載 [java] view plaincopyprint? 01.void setDirectoryForTemplateLoading(File dir); [java] view plaincopyprint? 01.void setClassForTemplateLoading(Class cl, String prefix); [java] view plaincopyprint? 01.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.二、從多個位置加載模板 [java] view plaincopyprint? 01.import freemarker.cache.*; // 模板加載器在這個包下 02.... 03.FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates")); 04.FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates")); 05.ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(),""); 06.TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2,ctl }; 07.MultiTemplateLoader mtl = new MultiTemplateLoader(loaders); 08.cfg.setTemplateLoader(mtl); 如今,FreeMarker 將會嘗試從/tmp/templates 目錄加載模板,若是在這個目錄下沒有發現請求的模板,它就會繼續嘗試從/usr/data/templates 目錄下加載,若是仍是沒有發現請求的模板,那麼它就會使用類加載器來加載模板。 5.三、封裝freemarker用於建立模板和加載模板 [java] view plaincopyprint? 01.package com.ajun.template.utils; 02. 03.import java.io.IOException; 04.import java.io.Writer; 05.import java.util.Locale; 06.import java.util.Map; 07. 08.import javax.servlet.ServletContext; 09. 10.import freemarker.template.Configuration; 11.import freemarker.template.DefaultObjectWrapper; 12.import freemarker.template.Template; 13.import freemarker.template.TemplateException; 14. 15./** 16. * @author ajun 17. * @http://blog.csdn.net/ajun_studio 18. **/ 19.public class FreeMarkertUtil { 20. 21. private static Configuration config = new Configuration(); 22. 23. /** 24. * @param templateName 模板名字 25. * @param root 模板根 用於在模板內輸出結果集 26. * @param out 輸出對象 具體輸出到哪裏 27. */ 28. public static void processTemplate(String templateName, Map<?,?> root, Writer out){ 29. try{ 30. //得到模板 31. Template template=config.getTemplate(templateName,"utf-8"); 32. //生成文件(這裏是咱們是生成html) 33. template.process(root, out); 34. out.flush(); 35. } catch (IOException e) { 36. e.printStackTrace(); 37. } catch (TemplateException e) { 38. e.printStackTrace(); 39. }finally{ 40. try { 41. out.close(); 42. out=null; 43. } catch (IOException e) { 44. e.printStackTrace(); 45. } 46. } 47. } 48. /** 49. * 初始化模板配置 50. * @param servletContext javax.servlet.ServletContext 51. * @param templateDir 模板位置 52. */ 53. public static void initConfig(ServletContext servletContext,String templateDir){ 54. config.setLocale(Locale.CHINA); 55. config.setDefaultEncoding("utf-8"); 56. config.setEncoding(Locale.CHINA, "utf-8"); 57. config.setServletContextForTemplateLoading(servletContext, templateDir); 58. config.setObjectWrapper(new DefaultObjectWrapper()); 59. } 60.} 5.四、例子介紹 會用freemarker.jar本身google下載吧。 這個例子中咱們會Freemarker生成一個html文件 包括html的頭部和尾部,已經body,這三個部分會分別對應三個模板文件,以下: 在模板內要想輸出結果集 能夠用相似於EL表達式輸出${} header.ftl [plain] view plaincopyprint? 01.companyName==>${h.companyName}<br/> 02.address==>${h.address}<br/> footer.ftl [plain] view plaincopyprint? 01.des==>${f.des}<br/> 02. 03.<a href="http://localhost/htmlpage/UpdateFooter.do"> 更新Footer </a> body.ftl,這個模板include以上兩個模板文件 [plain] view plaincopyprint? 01.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 02.<html> 03. <head> 04. <title>用戶列表</title> 05. 06. <meta http-equiv="pragma" content="no-cache"> 07. <meta http-equiv="cache-control" content="no-cache"> 08. <meta http-equiv="expires" content="0"> 09. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 10. <meta http-equiv="description" content="This is my page"> 11. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 12. <!-- 13. <link rel="stylesheet" type="text/css" href="styles.css"> 14. --> 15. 16. </head> 17. 18. <body> 19. <#include "header.ftl" parse=true encoding="utf-8"> 20. <hr/> 21. <a href="#">用戶列表</a><br/> 22. <table border="1"> 23. <tr> 24. <td>用戶名</td> 25. <td>年齡</td> 26. <td>生日</td> 27. <td>id</td> 28. <td>操做</td> 29. </tr> 30. <#list users as user> 31. <tr> 32. <td>${user.name}</td> 33. <td>${user.age}</td> 34. <td> 35. ${user.birthday?string("yyyy-MM-dd HH:mm:ss")} 36. </td> 37. <td>${user.id}</td> 38. <td><a href="http://localhost/htmlpage/DelUser.do?id=${user.id}">刪除</a></td> 39. </tr> 40. </#list> 41. 42. </table> 43.<hr/> 44. <#include "footer.ftl" parse=true encoding="utf-8"> 45. </body> 46.</html> 這三個模板對應的三個實體類 Footer.java [java] view plaincopyprint? 01.package com.ajun.template.bean; 02. 03./** 04. * @author ajun 05. * @http://blog.csdn.net/ajun_studio 06. **/ 07.public class Footer { 08. 09. private String des; 10. 11. public String getDes() { 12. return des; 13. } 14. 15. public void setDes(String des) { 16. this.des = des; 17. } 18. 19. 20.} Header.java [java] view plaincopyprint? 01.package com.ajun.template.bean; 02./** 03. * @author ajun 04. * @http://blog.csdn.net/ajun_studio 05. **/ 06.public class Header { 07. 08. private String companyName; 09. 10. private String address; 11. 12. public String getCompanyName() { 13. return companyName; 14. } 15. 16. public void setCompanyName(String companyName) { 17. this.companyName = companyName; 18. } 19. 20. public String getAddress() { 21. return address; 22. } 23. 24. public void setAddress(String address) { 25. this.address = address; 26. } 27. 28. 29. 30.} User.java [java] view plaincopyprint? 01.package com.ajun.template.bean; 02. 03.import java.util.Date; 04. 05.public class User { 06. 07. private Integer id; 08. 09. private String name ; 10. 11. private int age; 12. 13. private Date birthday; 14. 15. public String getName() { 16. return name; 17. } 18. 19. public void setName(String name) { 20. this.name = name; 21. } 22. 23. public int getAge() { 24. return age; 25. } 26. 27. public void setAge(int age) { 28. this.age = age; 29. } 30. 31. public Date getBirthday() { 32. return birthday; 33. } 34. 35. public void setBirthday(Date birthday) { 36. this.birthday = birthday; 37. } 38. 39. 40. public Integer getId() { 41. return id; 42. } 43. 44. public void setId(Integer id) { 45. this.id = id; 46. } 47. 48. public User(Integer id,String name, int age, Date birthday) { 49. super(); 50. this.name = name; 51. this.age = age; 52. this.birthday = birthday; 53. this.id = id; 54. } 55. 56. public User() { 57. super(); 58. } 59. 60. 61.} 下面模板一些業務邏輯操做,對這三個實體類 [java] view plaincopyprint? 01.package com.ajun.template.service; 02. 03.import com.ajun.template.bean.Footer; 04./** 05. * @author ajun 06. * @http://blog.csdn.net/ajun_studio 07. **/ 08.public class FooterService { 09. 10. private static Footer f = new Footer(); 11. static{ 12. f.setDes("北京-廊坊-好公司呢!!!!哇哈哈!!!"); 13. } 14. 15. public static void update(String des){ 16. f.setDes(des); 17. } 18. 19. public static Footer gerFooter(){ 20. return f; 21. } 22.} [java] view plaincopyprint? 01.package com.ajun.template.service; 02. 03.import com.ajun.template.bean.Header; 04./** 05. * @author ajun 06. * @http://blog.csdn.net/ajun_studio 07. **/ 08.public class HeaderService { 09. 10. private static Header h = new Header(); 11. 12. static{ 13. h.setAddress("北京朝陽CBD"); 14. h.setCompanyName("上海唐秀!!!"); 15. } 16. 17. public static void update(String address,String companyName){ 18. h.setAddress(address); 19. h.setCompanyName(companyName); 20. } 21. 22. public static Header getHeader(){ 23. return h; 24. } 25.} [java] view plaincopyprint? 01.package com.ajun.template.service; 02. 03.import java.util.ArrayList; 04.import java.util.Date; 05.import java.util.List; 06. 07.import com.ajun.template.bean.User; 08./** 09. * @author ajun 10. * @http://blog.csdn.net/ajun_studio 11. **/ 12.public class UserService { 13. 14. private static List<User> users = new ArrayList<User>(); 15. 16. static{ 17. for(int i=0;i<10;i++){ 18. User u = new User(i,"ajun"+i,i+10,new Date()); 19. users.add(u); 20. } 21. } 22. 23. public static List<User> getUsers(){ 24. return users; 25. } 26. 27. public static void delete(int index){ 28. for(int i=0 ;i<users.size();i++){ 29. User u = users.get(i); 30. if(u.getId()==index){ 31. users.remove(u); 32. //users.remove(index); 33. } 34. } 35. } 36.} 上面主要是模板你的一些業務和dao層得操做,所以沒有涉及數據庫的操做,主要是爲實驗。 生成html對外調用的方法,會用到FreeMarkertUtil這個類 這個類得代碼上面已經給出。 [java] view plaincopyprint? 01.package com.ajun.template.client; 02. 03.import java.io.Writer; 04.import java.util.HashMap; 05.import java.util.List; 06.import java.util.Map; 07. 08.import com.ajun.template.bean.Footer; 09.import com.ajun.template.bean.Header; 10.import com.ajun.template.bean.User; 11.import com.ajun.template.service.FooterService; 12.import com.ajun.template.service.HeaderService; 13.import com.ajun.template.service.UserService; 14.import com.ajun.template.utils.FreeMarkertUtil; 15. 16./** 17. * @author ajun 18. * @http://blog.csdn.net/ajun_studio 19. **/ 20.public class ProcessClient { 21. 22. private static Map<String,Object> root = new HashMap<String,Object>(); 23. 24. /** 25. * 調用FreeMarkertUtil.java 26. * FreeMarkertUtil.processTemplate("body.ftl", root, out); 27. * 來生成html文件 28. * @param out 29. */ 30. public static void processBody(Writer out){ 31. Header h = HeaderService.getHeader(); 32. root.put("h", h); 33. Footer f = FooterService.gerFooter(); 34. root.put("f", f); 35. List<User> users = UserService.getUsers(); 36. root.put("users", users); 37. FreeMarkertUtil.processTemplate("body.ftl", root, out); 38. } 39. 40.} 此時我會提供一個servlet在客戶端進行第一次請求的時候 我會調用這個ProcessClient來生成html頁面,以後每次訪問就能夠直接訪問html,來作到真正的靜態化了 [java] view plaincopyprint? 01.package com.ajun.template.servlet; 02. 03.import java.io.File; 04.import java.io.FileOutputStream; 05.import java.io.IOException; 06.import java.io.OutputStreamWriter; 07.import java.io.Writer; 08. 09.import javax.servlet.ServletConfig; 10.import javax.servlet.ServletException; 11.import javax.servlet.http.HttpServlet; 12.import javax.servlet.http.HttpServletRequest; 13.import javax.servlet.http.HttpServletResponse; 14. 15.import com.ajun.template.client.ProcessClient; 16.import com.ajun.template.utils.DirectoryFilter; 17.import com.ajun.template.utils.FreeMarkertUtil; 18. 19./** 20. * @author ajun 21. * @http://blog.csdn.net/ajun_studio 22. **/ 23.public class Index extends HttpServlet { 24. 25. private static final long serialVersionUID = 7474850489594438527L; 26. 27. public Index() { 28. super(); 29. } 30. 31. 32. public void doGet(HttpServletRequest request, HttpServletResponse response) 33. throws ServletException, IOException { 34. 35. this.doPost(request, response); 36. } 37. 38. 39. public void doPost(HttpServletRequest request, HttpServletResponse response) 40. throws ServletException, IOException { 41. //html生成以後存放的路徑 42. String dirPath = request.getSession().getServletContext().getRealPath("/templates/html"); 43. File path = new File(dirPath); 44. //生成的文件的名字 45. String indexFileName = "index.html"; 46. /** 47. * 判斷是否已經存在該html文件,存在了就直接訪問html ,不存在生成html文件 48. */ 49. String[] indexfileList = path.list(new DirectoryFilter(indexFileName)); 50. if(indexfileList.length<=0){ 51. Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+indexFileName),"UTF-8"); 52. //生成html文件 53. ProcessClient.processBody(out); 54. request.getRequestDispatcher("/templates/html/index.html").forward(request, response); 55. }else{ 56. request.getRequestDispatcher("/templates/html/"+indexfileList[0]).forward(request, response); 57. } 58. 59. 60. } 61. 62. 63. 64. /** 65. * 初始化模板配置,供之後得到模板,在init里加載也主要是爲保證Configuration實例惟一 66. */ 67. public void init(ServletConfig config) throws ServletException { 68. String templateDir = config.getInitParameter("templateDir"); 69. FreeMarkertUtil.initConfig(config.getServletContext(), templateDir); 70. } 71. 72. 73.} web.xml配置 [html] view plaincopyprint? 01.<servlet> 02. <description>This is the description of my J2EE component</description> 03. <display-name>This is the display name of my J2EE component</display-name> 04. <servlet-name>Index</servlet-name> 05. <servlet-class>com.ajun.template.servlet.Index</servlet-class> 06. <init-param> 07. <param-name>templateDir</param-name>模板存放位置,是基於app的根目錄的 08. <param-value>/templates</param-value> 09. </init-param> 10. <load-on-startup>3</load-on-startup>爲了啓動的時候初始化模板配置 11. </servlet> 12. 13. <servlet-mapping> 14. <servlet-name>Index</servlet-name> 15. <url-pattern>/Index.do</url-pattern> 16. </servlet-mapping> 部署到tomcat上,輸入:http://localhost/htmlpage/Index.do 頁面效果: 頁面是作好了,可是內容變化了 ,更新怎麼辦呢,我這裏是當列表內容變化以後 ,我是刪除原來的html ,利用模板而後從新生成的符合新結果的html頁面 當我刪除一條的時候,我會從新生成html頁面,可是因爲瀏覽器緩存的緣由,便是你刪除了,從新生成了新html頁面,但是瀏覽器仍是保存原來的頁面,不刷新兩次是不行的, 這裏我採用的沒更新的時候,都會給這個html改個名字,讓瀏覽器去加載最新的,就能夠了 具體的刪除操做以下: [java] view plaincopyprint? 01.package com.ajun.template.servlet; 02. 03.import java.io.File; 04.import java.io.FileOutputStream; 05.import java.io.IOException; 06.import java.io.OutputStreamWriter; 07.import java.io.Writer; 08.import java.util.UUID; 09. 10.import javax.servlet.ServletException; 11.import javax.servlet.http.HttpServlet; 12.import javax.servlet.http.HttpServletRequest; 13.import javax.servlet.http.HttpServletResponse; 14. 15.import com.ajun.template.client.ProcessClient; 16.import com.ajun.template.service.UserService; 17.import com.ajun.template.utils.DirectoryFilter; 18./** 19. * @author ajun 20. * @http://blog.csdn.net/ajun_studio 21. **/ 22.public class DelUser extends HttpServlet { 23. 24. 25. public void doGet(HttpServletRequest request, HttpServletResponse response) 26. throws ServletException, IOException { 27. this.doPost(request, response); 28. } 29. 30. //刪除用戶 31. public void doPost(HttpServletRequest request, HttpServletResponse response) 32. throws ServletException, IOException { 33. 34. String id = request.getParameter("id"); 35. UserService.delete(Integer.valueOf(id)); 36. 37. //生成html的位置 38. String dirPath = request.getSession().getServletContext().getRealPath("/templates/html"); 39. //文件名字 40. String indexFileName = "index.html"; 41. 42. //刪除原來的文件 43. delOldHtml(dirPath,indexFileName); 44. 45. //防止瀏覽器緩存,用於從新生成新的html 46. UUID uuid = UUID.randomUUID(); 47. Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+uuid+indexFileName),"UTF-8"); 48. ProcessClient.processBody(out); 49. response.sendRedirect("templates/html/"+uuid+"index.html"); 50. } 51. 52. /** 53. * 刪除原來的html文件 54. * @param htmlDir 55. * @param htmlName 56. */ 57. private void delOldHtml(String htmlDir,String htmlName){ 58. File path = new File(htmlDir); 59. String[] indexfileList = path.list(new DirectoryFilter(htmlName)); 60. if(indexfileList.length>=0){ 61. for(String f:indexfileList){ 62. File delf = new File(htmlDir+"/"+f); 63. delf.delete(); 64. } 65. } 66. } 67. 68.} 經過以上操做,每次更新html,就能夠不解決瀏覽器緩存的問題了。 還有一個工具類須要介紹,就是判斷是否已經生成了特定的html文件的java類 [java] view plaincopyprint? 01.package com.ajun.template.utils; 02. 03.import java.io.File; 04.import java.io.FilenameFilter; 05./** 06. * @author ajun 07. * @http://blog.csdn.net/ajun_studio 08. **/ 09.public class DirectoryFilter implements FilenameFilter { 10. 11. String myString; 12. public DirectoryFilter(String myString) 13. { 14. this.myString = myString; 15. } 16. 17. public boolean accept(File dir,String name) 18. { //FilenameFilter.accept(File dir, String name) 19. // 測試指定文件是否應該包含在某一文件列表中。 20. String f= new File(name).getName(); 21. if(f.contains(myString) || f.equals(myString)){ 22. return true; 23. } 24. return false; 25. } 26. 27.} 到這裏整個靜態化就完成了,靜態化更新機制,是根據你本身項目的業務進行定製的,能夠定時生成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