使用Freemarker 实现JSP页面的静态化
DFRJasper
9年前
一、原理
Freemarker 生成静态页面,首先需要使用自己定义的模板页面,这个模板页面可以是最最普通的 html ,也可以是嵌套 freemarker 中的 取值表达式, 标签或者自定义标签等等,然后后台读取这个模板页面,解析其中的标签完成相对应的操作, 然后采用键值对的方式传递参数替换模板中的的取值表达式,做完之后 根据配置的路径生成一个新的 html 页面, 以达到静态化访问的目的。
二、Freemaker 提供的标签
Freemarker 提供了很多有用 常用的标签, 具体可以分为三个部分:Freemarker 标签都是 类似 Html 标签 , 不同的是它是为了与 HTML 标记区分,用 # 开始。例如 <# 标签名称 > ;${value} 表示输出变量名的内容 ;注释:包含在 <#-- 和 --> (而不是 <!-- 和 --> )之间;
三、Freemaker 实现网页静态化 DEMO
Freemarker 是一种基于模板的、用来生成输出文本的通用工具,所以我们必须要定制符合自己业务的模板出来,然后生成的我们得 html 页面
这个例子中我们会 Freemarker 生成一个 html 文件 包括 html 的头部和尾部, 以及body ,这三个部分会分别对应三个模板文件 :
Body.ftl <!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:8082/JspToHtml/DelUser.do?id=${user.id}">删除</a></td> </tr> </#list> </table> <hr/> <#include "footer.ftl" parse=true encoding="utf-8"> </body> </html> Footer.ftl ${f.des}<br/> Header.ftl company:${h.companyName}<br/> address:${h.address}<br/> 它们对于的实体类分别是: User.java package com.cys.jsptohtml.schema; 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(); } } Header.java package com.cys.jsptohtml.schema; 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; } } Footer.java package com.cys.jsptohtml.schema; 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; } } 对应的Service有(在Service中模仿了数据库操作): UserService.java: package com.cys.jsptohtml.service; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.cys.jsptohtml.schema.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,"cys"+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); } } } } HeaderService.java package com.cys.jsptohtml.service; import com.cys.jsptohtml.schema.Header; public class HeaderService { private static Header h = new Header(); static{ h.setAddress("中关村东路"); h.setCompanyName("中科软"); } public static void update(String address,String companyName){ h.setAddress(address); h.setCompanyName(companyName); } public static Header getHeader(){ return h; } } FooterService.java package com.cys.jsptohtml.service; import com.cys.jsptohtml.schema.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; } } Servlet操作: DelUser.java package com.cys.jsptohtml.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; 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.cys.jsptohtml.service.UserService; import com.cys.jsptohtml.util.ProcessClient; /** * @author cys **/ @SuppressWarnings("serial") 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 { System.out.println("Del dopost"); String id = request.getParameter("id"); UserService.delete(Integer.valueOf(id)); //生成html的位置 String dirPath = request.getSession().getServletContext().getRealPath("/templateDir/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, "body.ftl"); response.sendRedirect("templateDir/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 FilenameFilter(){ public boolean accept(File dir, String name) { return name.endsWith(".html"); } }); if(indexfileList.length>0){ for(String f:indexfileList){ File delf = new File(htmlDir+"/"+f); delf.delete(); } } } } JspToHtmlServlet.java package com.cys.jsptohtml.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.cys.jsptohtml.util.ProcessClient; public class JspToHtmlServlet extends HttpServlet{ private static final long serialVersionUID = 1L; public JspToHtmlServlet() { super(); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { System.out.println("doget"); this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doPost"); // html生成之后存放的路径 String dirPath = request.getSession().getServletContext().getRealPath("/templateDir/html"); File path = new File(dirPath); // 生成的文件的名字 String indexFileName = "index.html"; /** * 判断是否已经存在该html文件,存在了就直接访问html ,不存在生成html文件 */ String[] indexfileList = path.list(new FilenameFilter(){ public boolean accept(File dir, String name) { return name.endsWith(".html"); } }); System.out.println(indexfileList); if(indexfileList.length<=0){ Writer out = new OutputStreamWriter(new FileOutputStream(dirPath+"/"+indexFileName),"UTF-8"); // 生成html文件 ProcessClient.processBody(out,"body.ftl"); request.getRequestDispatcher("/templateDir/html/index.html").forward(request, response); }else{ request.getRequestDispatcher("/templateDir/html/"+indexfileList[0]).forward(request, response); } } } Login.java(该类用于测试环境,与静态化无关) package com.cys.jsptohtml.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Login extends HttpServlet{ private static final long serialVersionUID = 7474850489594438527L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); String action = request.getParameter("action"); if("login_input".equals(action)) { request.getRequestDispatcher("login.jsp").forward(request , response); } else if("login".equals(action)) { String name = request.getParameter("name"); String password = request.getParameter("password"); System.out.println("name->" + name + ",password->" + password); } } } Jsp页面: Index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <p>用Maven创建web项目,测试Servlet</p> <a href="demo?action=login_input">登录(demo?action=login_input)</a> <a href="index?action=login_input">静态化</a> </body> </html> Login.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="demo?action=login" method="post"> Name:<input type="text" name="name" /> Password:<input type="password" name="password" /> <input type="submit" value="登录" /> </form> </body> </html> Web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <javaee:display-name>Archetype Created Web Application</javaee:display-name> <servlet> <javaee:description></javaee:description> <javaee:display-name>ServletDemo</javaee:display-name> <servlet-name>ServletDemo</servlet-name> <servlet-class>com.cys.jsptohtml.servlet.Login</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/demo</url-pattern> </servlet-mapping> <servlet> <servlet-name>Index</servlet-name> <servlet-class>com.cys.jsptohtml.servlet.JspToHtmlServlet</servlet-class> <load-on-startup>3</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Index</servlet-name> <url-pattern>/index</url-pattern> </servlet-mapping> <servlet> <servlet-name>DelUser</servlet-name> <servlet-class>com.cys.jsptohtml.servlet.DelUser</servlet-class> <load-on-startup>3</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DelUser</servlet-name> <url-pattern>/DelUser.do</url-pattern> </servlet-mapping> </web-app> Util类: FreeMarkertUtil .java package com.cys.jsptohtml.util; import java.io.IOException; import java.io.Writer; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; public class FreeMarkertUtil { public Template getTemplate(String name) { try { // 通过Freemaker的Configuration读取相应的ftl Configuration cfg = new Configuration(); // 设定去哪里读取相应的ftl模板文件 cfg.setClassForTemplateLoading(this.getClass(), "ftl"); // 在模板文件目录中找到名称为name的文件 Template temp = cfg.getTemplate(name); System.out.println(temp.getName()); return temp; } catch (IOException e) { e.printStackTrace(); } return null; } /** * @param templateName 模板名字 * @param root 模板根 用于在模板内输出结果集 * @param out 输出对象 具体输出到哪里 */ public void processTemplate(Template template,Map<?,?> root, Writer out){ try{ System.out.println("processTemplate"); 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(); } } } } 在该类中加载模板的方式有多种: void setDirectoryForTemplateLoading(File dir);// 根据全路径加载 void setClassForTemplateLoading(Class cl, String prefix);//根据类的路径加载,prefix为模板前缀 void setServletContextForTemplateLoading(Object servletContext, String path); //根据web上下文 从多地址加载模板 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 目录下加载,如果还是没有发现请求的模板,那么它就会使用类加载器来加载模板。 ProcessClient .java package com.cys.jsptohtml.util; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import com.cys.jsptohtml.schema.Footer; import com.cys.jsptohtml.schema.Header; import com.cys.jsptohtml.schema.User; import com.cys.jsptohtml.service.FooterService; import com.cys.jsptohtml.service.HeaderService; import com.cys.jsptohtml.service.UserService; import freemarker.template.Template; /** * @author cys **/ 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,String filename){ 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 freeMarkertUtil = new FreeMarkertUtil(); Template template = freeMarkertUtil.getTemplate(filename); freeMarkertUtil.processTemplate(template, root, out); } } Pom.xml(对应的jar包,以及插件): <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cys</groupId> <artifactId>JspToHtml</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>JspToHtml Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency> </dependencies> <build> <finalName>codingdream</finalName> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <path></path> <port>8082</port> <uriEncoding>UTF-8</uriEncoding> <url>http://localhost:8087/codingdream</url> <server>tomcat6</server> </configuration> </plugin> </plugins> </build> </project>
四、运行结果
参考:http://freemarker.incubator.apache.org/
http://blog.csdn.net/ajun_studio/article/details/6932185/