zoukankan      html  css  js  c++  java
  • 利用freemarker 静态化网页

    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 目录下的某个位置存储模板文件,这样模板源文件就不会偶然地被执行到,这种机制对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;
    
    /**
     * @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.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;
    
    /**
     * @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/

    中文文档:https://sourceforge.net/projects/freemarker/files/chinese-manual/FreeMarker_Manual_zh_CN.pdf/download

    本文转自:http://blog.csdn.net/ajun_studio/article/details/6932185

  • 相关阅读:
    51nod 2080 最长上升子序列
    common js
    es Module
    git关于分支的常用操作
    react实现浏览器的返回、前进、刷新,关闭拦截
    Blob,ArrayBuffer,FileReader,FormData,Buffer的理解
    memo、useCallback、useMemo三者的区别
    npm 和 yarn的全局安装位置
    react中单行文本溢出省略号
    react中基于styled-components组件的一像素边框问题
  • 原文地址:https://www.cnblogs.com/dreammyle/p/4616247.html
Copyright © 2011-2022 走看看