1. Freemarker

页面静态化技术 将原来的动态页面 改为静态网页 这样用户在访问网页时 不必每次查询动态数据库过程

我们可以通过专门的页面静态化技术帮我们生成所需的静态HTML页面,例如:Freemarker、thymeleaf等。

08. Freemarker - 图1

坐标

  1. <dependency>
  2. <groupId>org.freemarker</groupId>
  3. <artifactId>freemarker</artifactId>
  4. <version>2.3.23</version>
  5. </dependency>

2. 创建模板

模板文件中有四种元素:

  1. 文本,直接输出的部分
  2. 注释,即<#—…—>格式不会输出
  3. 插值(Interpolation):即${..}部分,将使用数据模型中的部分替代输出
  4. FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出

Freemarker的模板文件后缀可以任意,一般建议为ftl。

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>Freemarker入门</title>
  5. </head>
  6. <body>
  7. <#--我只是一个注释,我不会有任何输出 -->
  8. ${name}你好,${message}
  9. </body>
  10. </html>

3. 生成文件

第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是 freemarker的版本号。

第二步:设置模板文件所在的路径。

第三步:设置模板文件使用的字符集。一般就是 utf-8。

第四步:加载一个模板,创建一个模板对象。

第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。

第六步:创建一个 Writer 对象,一般创建 FileWriter 对象,指定生成的文件名。

第七步:调用模板对象的 process 方法输出文件。

第八步:关闭流。

  1. public static void main(String[] args) throws Exception{
  2. //1.创建配置类
  3. Configuration configuration=new Configuration(Configuration.getVersion());
  4. //2.设置模板所在的目录
  5. configuration.setDirectoryForTemplateLoading(new File("D:\\ftl"));
  6. //3.设置字符集
  7. configuration.setDefaultEncoding("utf-8");
  8. //4.加载模板
  9. Template template = configuration.getTemplate("test.ftl");
  10. //5.创建数据模型
  11. Map map=new HashMap();
  12. map.put("name", "张三");
  13. map.put("message", "欢迎来到传智播客!");
  14. //6.创建Writer对象
  15. Writer out =new FileWriter(new File("d:\\test.html"));
  16. //7.输出
  17. template.process(map, out);
  18. //8.关闭Writer对象
  19. out.close();
  20. }

4. Freemarker指令

4.1. assign指令

assign指令用于在页面上定义一个变量

(1)定义简单类型

  1. <#assign linkman="周先生">
  2. 联系人:${linkman}

(2)定义对象类型

  1. <#assign info={"mobile":"13812345678",'address':'北京市昌平区'} >
  2. 电话:${info.mobile} 地址:${info.address}

4.2. include指令

include指令用于模板文件的嵌套

(1)创建模板文件head.ftl

  1. <h1>黑马程序员</h1>

(2)修改入门案例中的test.ftl,在test.ftl模板文件中使用include指令引入上面的模板文件

  1. <#include "head.ftl"/>

4.3. if指令

if指令用于判断

(1)在模板文件中使用if指令进行判断

  1. <#if success=true>
  2. 你已通过实名认证
  3. <#else>
  4. 你未通过实名认证
  5. </#if>

(2)在java代码中为success变量赋值

  1. map.put("success", true);

在freemarker的判断中,可以使用= 也可以使用==

4.4. list指令

list指令用于遍历

(1)在模板文件中使用list指令进行遍历

  1. <#list goodsList as goods>
  2. 商品名称: ${goods.name} 价格:${goods.price}<br>
  3. </#list>

(2)在java代码中为goodsList赋值

  1. List goodsList=new ArrayList();
  2. Map goods1=new HashMap();
  3. goods1.put("name", "苹果");
  4. goods1.put("price", 5.8);
  5. Map goods2=new HashMap();
  6. goods2.put("name", "香蕉");
  7. goods2.put("price", 2.5);
  8. Map goods3=new HashMap();
  9. goods3.put("name", "橘子");
  10. goods3.put("price", 3.2);
  11. goodsList.add(goods1);
  12. goodsList.add(goods2);
  13. goodsList.add(goods3);
  14. map.put("goodsList", goodsList);

5. 环境搭建

5.1. 坐标

  1. <dependency>
  2. <groupId>org.freemarker</groupId>
  3. <artifactId>freemarker</artifactId>
  4. <version>2.3.23</version>
  5. </dependency>

5.2. 创建模板文件

mobile_setmeal.ftl

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
  7. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
  8. <meta name="description" content="">
  9. <meta name="author" content="">
  10. <link rel="icon" href="../img/asset-favico.ico">
  11. <title>预约</title>
  12. <link rel="stylesheet" href="../css/page-health-order.css" />
  13. </head>
  14. <body data-spy="scroll" data-target="#myNavbar" data-offset="150">
  15. <div class="app" id="app">
  16. <!-- 页面头部 -->
  17. <div class="top-header">
  18. <span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span>
  19. <span class="center">传智健康</span>
  20. <span class="f-right"><i class="icon-more"></i></span>
  21. </div>
  22. <!-- 页面内容 -->
  23. <div class="contentBox">
  24. <div class="list-column1">
  25. <ul class="list">
  26. <#list setmealList as setmeal>
  27. <li class="list-item">
  28. <a class="link-page" href="setmeal_detail_${setmeal.id}.html">
  29. <img class="img-object f-left"
  30. src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}"
  31. alt="">
  32. <div class="item-body">
  33. <h4 class="ellipsis item-title">${setmeal.name}</h4>
  34. <p class="ellipsis-more item-desc">${setmeal.remark}</p>
  35. <p class="item-keywords">
  36. <span>
  37. <#if setmeal.sex == '0'>
  38. 性别不限
  39. <#else>
  40. <#if setmeal.sex == '1'>
  41. <#else>
  42. </#if>
  43. </#if>
  44. </span>
  45. <span>${setmeal.age}</span>
  46. </p>
  47. </div>
  48. </a>
  49. </li>
  50. </#list>
  51. </ul>
  52. </div>
  53. </div>
  54. </div>
  55. <!-- 页面 css js -->
  56. <script src="../plugins/vue/vue.js"></script>
  57. <script src="../plugins/vue/axios-0.18.0.js"></script>
  58. </body>

mobile_setmeal_detail.ftl

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
  7. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
  8. <meta name="description" content="">
  9. <meta name="author" content="">
  10. <link rel="icon" href="../img/asset-favico.ico">
  11. <title>预约详情</title>
  12. <link rel="stylesheet" href="../css/page-health-orderDetail.css" />
  13. <script src="../plugins/vue/vue.js"></script>
  14. <script src="../plugins/vue/axios-0.18.0.js"></script>
  15. <script src="../plugins/healthmobile.js"></script>
  16. </head>
  17. <body data-spy="scroll" data-target="#myNavbar" data-offset="150">
  18. <div id="app" class="app">
  19. <!-- 页面头部 -->
  20. <div class="top-header">
  21. <span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span>
  22. <span class="center">传智健康</span>
  23. <span class="f-right"><i class="icon-more"></i></span>
  24. </div>
  25. <!-- 页面内容 -->
  26. <div class="contentBox">
  27. <div class="card">
  28. <div class="project-img">
  29. <img src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}"
  30. width="100%" height="100%" />
  31. </div>
  32. <div class="project-text">
  33. <h4 class="tit">${setmeal.name}</h4>
  34. <p class="subtit">${setmeal.remark}</p>
  35. <p class="keywords">
  36. <span>
  37. <#if setmeal.sex == '0'>
  38. 性别不限
  39. <#else>
  40. <#if setmeal.sex == '1'>
  41. <#else>
  42. </#if>
  43. </#if>
  44. </span>
  45. <span>${setmeal.age}</span>
  46. </p>
  47. </div>
  48. </div>
  49. <div class="table-listbox">
  50. <div class="box-title">
  51. <i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i>
  52. <span>套餐详情</span>
  53. </div>
  54. <div class="box-table">
  55. <div class="table-title">
  56. <div class="tit-item flex2">项目名称</div>
  57. <div class="tit-item flex3">项目内容</div>
  58. <div class="tit-item flex3">项目解读</div>
  59. </div>
  60. <div class="table-content">
  61. <ul class="table-list">
  62. <#list setmeal.checkGroups as checkgroup>
  63. <li class="table-item">
  64. <div class="item flex2">${checkgroup.name}</div>
  65. <div class="item flex3">
  66. <#list checkgroup.checkItems as checkitem>
  67. <label>
  68. ${checkitem.name}
  69. </label>
  70. </#list>
  71. </div>
  72. <div class="item flex3">${checkgroup.remark}</div>
  73. </li>
  74. </#list>
  75. </ul>
  76. </div>
  77. <div class="box-button">
  78. <a @click="toOrderInfo()" class="order-btn">立即预约</a>
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. <script>
  85. var vue = new Vue({
  86. el:'#app',
  87. methods:{
  88. toOrderInfo(){
  89. window.location.href = "orderInfo.html?id=${setmeal.id}";
  90. }
  91. }
  92. });
  93. </script>
  94. </body>

5.3. 配置文件

在health_service_provider工程中创建属性文件freemarker.properties

对应为静态网页的访问触发路径

  1. out_put_path=D:/ideaProjects/health_parent/health_mobile/src/main/webapp/pages

在health_service_provider工程的Spring配置文件中配置

  1. <bean id="freemarkerConfig"
  2. class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
  3. <!--指定模板文件所在目录-->
  4. <property name="templateLoaderPath" value="/WEB-INF/ftl/" />
  5. <!--指定字符集-->
  6. <property name="defaultEncoding" value="UTF-8" />
  7. </bean>
  8. <context:property-placeholder location="classpath:freemarker.properties"/>

5.4. 生成静态页面

注入freemark

  1. @Autowired
  2. private JedisPool jedisPool;
  3. @Value("${out_put_path}")//从属性文件读取输出目录的路径
  4. private String outputpath ;

生成方法

  1. @Value("${out_put_path}")//从属性文件读取输出目录的路径
  2. private String outputpath ;
  3. //新增套餐,同时关联检查组
  4. public void add(Setmeal setmeal, Integer[] checkgroupIds) {
  5. setmealDao.add(setmeal);
  6. Integer setmealId = setmeal.getId();//获取套餐id
  7. this.setSetmealAndCheckGroup(setmealId,checkgroupIds);
  8. //完成数据库操作后需要将图片名称保存到redis
  9. jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());
  10. //新增套餐后需要重新生成静态页面
  11. generateMobileStaticHtml();
  12. }
  13. //生成静态页面
  14. public void generateMobileStaticHtml() {
  15. //准备模板文件中所需的数据
  16. List<Setmeal> setmealList = this.findAll();
  17. //生成套餐列表静态页面
  18. generateMobileSetmealListHtml(setmealList);
  19. //生成套餐详情静态页面(多个)
  20. generateMobileSetmealDetailHtml(setmealList);
  21. }
  22. //生成套餐列表静态页面
  23. public void generateMobileSetmealListHtml(List<Setmeal> setmealList) {
  24. Map<String, Object> dataMap = new HashMap<String, Object>();
  25. dataMap.put("setmealList", setmealList);
  26. this.generateHtml("mobile_setmeal.ftl","m_setmeal.html",dataMap);
  27. }
  28. //生成套餐详情静态页面(多个)
  29. public void generateMobileSetmealDetailHtml(List<Setmeal> setmealList) {
  30. for (Setmeal setmeal : setmealList) {
  31. Map<String, Object> dataMap = new HashMap<String, Object>();
  32. dataMap.put("setmeal", this.findById(setmeal.getId()));
  33. this.generateHtml("mobile_setmeal_detail.ftl",
  34. "setmeal_detail_"+setmeal.getId()+".html",
  35. dataMap);
  36. }
  37. }
  38. public void generateHtml(String templateName,String htmlPageName,Map<String, Object> dataMap){
  39. Configuration configuration = freeMarkerConfigurer.getConfiguration();
  40. Writer out = null;
  41. try {
  42. // 加载模版文件
  43. Template template = configuration.getTemplate(templateName);
  44. // 生成数据
  45. File docFile = new File(outputpath + "\\" + htmlPageName);
  46. out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
  47. // 输出文件
  48. template.process(dataMap, out);
  49. } catch (Exception e) {
  50. e.printStackTrace();
  51. } finally {
  52. try {
  53. if (null != out) {
  54. out.flush();
  55. }
  56. } catch (Exception e2) {
  57. e2.printStackTrace();
  58. }
  59. }
  60. }

更改index跳转为m_setmeal(静态套餐列表 而里详情超链接面访问的也是静态生成详情网页)

  1. <a href="/pages/m_setmeal.html" class="link-page">