一、原理
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>
四、运行结果
maven相关问题请参考:http://www.cnblogs.com/caoyusongnet/p/5150882.html
参考:http://freemarker.incubator.apache.org/
http://blog.csdn.net/ajun_studio/article/details/6932185/