微服务[学成在线] day04:页面静态化

从原理到实战:数字预失真(DPD)如何重塑现代无线通信 本文深入探讨了数字预失真(DPD)技术在现代无线通信中的关键作用,从原理到实战全面解析其如何解决功率放大器(PA)的非线性失真问题。通过AM-AM/AM-PM曲线分析、记忆效应处理及5G基站DPD实现案例,展示了DPD技术如何显著提升信号质量与系统效率。同时,文章还展望了AI赋能和芯片级集成等前沿进展,为通信工程师提供实用指南。 阅读详情

😎 知识点概览

为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。

本章节为【学成在线】项目的 day04 的内容

  • 页面静态化的基本概念
  • Freemarker 框架基础入门
  • 使用 MongoDB 提供的 GridFS 来实现页面数据的分布式储存
  • 使用 freemarker 框架实现页面静态化以及发布

目录

内容会比较多,小伙伴们可以根据目录进行按需查阅。

一、页面静态化需求

概述

  1. 为什么要进行页面管理?

    本项目的 cms 系统的功能就是根据运营的需要,对门户等子系统的部分页面进行管理,从而实现快速的根据用户需求修改页面的内容并上线需求。

  2. 如何修改页面的内容?

    在开发中修改页面的内容是需要人工编写 html 以及 js 文件, cms 系统是通过程序自动化的对页面的内容进行修改,通过页面静态化技术生成 html 页面。

  3. 如何对页面进行静态化?

    一个页面等于 模板 + 数据,在添加页面的时候我们选择了页面的模板。

  4. 页面静态化以及页面发布的流程

    发布流程图

业务流程

业务流程如下

  1. 获取模型数据
  2. 制作模板
  3. 对页面进行静态化
  4. 将静态化生成的 html 页面存放在文件系统中
  5. 将存放在文件系统的 html 页面发布到服务器

二、FreeMarker研究

0x01 FreeMarker介绍

  1. freemarker 是一个用 Java 开发的模板引擎

    常用的 java 模板引擎还有哪些?

    Jsp、Freemarker、Thymeleaf、Velocity 等

  2. 模板+数据模型 = 输出

    freemarker 并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出具体的(通常为 html ,也可以生成其他格式的文件)

    1. 数据模型

      数据模型在 java 中可以是基本类型也可以是 List、Map、Pojo 等复杂类型

    2. 来自官方的例子:(https://freemarker.apache.org/docs/dgui_quickstart_basics.html)

      数据模型:

      模板:

      输出:

0x02 FreeMark 快速入门

freemarker 作为 Springmvc 一种视图的格式,默认情况下, SpringMvc 支持 freemarker 视图格式。

需要创建 Spring boot + Freemarker工程用于测试的模板。

1、创建测试工程

我们在原有的工程下,创建一个test-freemarker 的 maven 模块,测试工程专门用于freemarker的功能测试与模板的测试。

pom如下

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xc-framework-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../xc-framework-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>test-freemarker</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>

2、配置文件

配置 application.yml 和 logback-spring.xml,从 cms 工程拷贝这两个文件,进行更改, logback-spring.xml 无需更改,application.yml 内容如下:

server:
  port: 8088 #服务端口
spring:
  application:
    name: test‐freemarker #指定服务名
  freemarker:
    cache: false #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便

3、创建模型类

在 freemarker 的测试工程下创建模型类型用于测试;

创建 com.xuecheng.test.freemarker.model 包,并构建一个 Student 模型类用于后面的测试

package com.xuecheng.test.freemarker.model;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.List;

@Data
@ToString
public class Student {
    private String name;//姓名
    private int age;//年龄
    private Date birthday;//生日
    private Float money;//钱包
    private List<Student> friends;//朋友列表
    private Student bestFriend;//最好的朋友
}

4、创建模板

在 src/main/resources 下创建 templates,此目录为 freemarker 的默认模板存放目录;

在 templates 下创建模板文件 test1.ftl,模板中的 ${name} 最终会被freemarker 替换成具体的数据。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf‐8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

5、 创建 controller

创建 Controller 类,向 Map 中添加 name,最后返回模板文件。

@RequestMapping("/freemarker")
@Controller  //这里主要不要使用RestController,RestController默认返回json数据,使模板引擎失效
public class FreemarkerController {

    /**
     * 测试1
     * @param map freemarker引擎会自动从Map形参中读取变量进行渲染
     * @return
     */
    @RequestMapping("/test1")
    public String freemarker(Map<String,Object> map){
        map.put("name", "黑马程序员");
        //返回模板文件名称
        return "test1";
    }
}

6、创建启动类

package com.xuecheng.test.freemarker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FreemarkerTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(FreemarkerTestApplication.class,args);
    }
}

7、测试

访问 http://localhost:8088/freemarker/test1 测试

0x03 Freemarker 基础语法

1、核心指令

数据模型

Freemarker 静态化依赖数据模型和模板,下边定义数据模型;

下边方法形参 map 即为 freemarker 静态化所需要的数据模型,在 map 中填充数据:

package com.xuecheng.test.freemarker.controller;
import com.xuecheng.test.freemarker.model.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RequestMapping("/freemarker")
@Controller  //这里主要不要使用RestController,RestController默认返回json数据,使模板引擎失效
public class FreemarkerController {

    /**
     * 测试1
     * @param map freemarker引擎会自动从Map形参中读取变量进行渲染
     * @return
     */
    @RequestMapping("/test1")
    public String freemarker(Map<String,Object> map){
        map.put("name", "黑马程序员");
        //学生对象1
        Student stu1 = new Student();
        stu1.setName("小明");
        stu1.setAge(18);
        stu1.setMoney(1888.123f);
        stu1.setBirthday(new Date());
        //学生对象2
        Student stu2 = new Student();
        stu2.setName("小红");
        stu2.setAge(22);
        stu2.setMoney(1888.123f);
        stu2.setBirthday(new Date());
        //创建一个List对象,用于储存上面这两个学生对象
        ArrayList<Object> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        //向数据模型中放入List
        map.put("stus",stus);
        //返回模板文件名称
        return "test1";
    }
}
List指令

本节定义freemarker模板,模板中使用freemarker的指令,关于freemarker的指令需要知道:

1、注释,即<#‐‐和‐‐>,介于其之间的内容会被freemarker忽略

2、插值(Interpolation):即${..}部分,freemarker会用真实的值代替${..}

3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。

4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容

在 test1.ftl 模板中使用list指令遍历数据模型中的数据:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf‐8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!

<p>在test1.ftl模板中使用list指令遍历数据模型中的数据:</p>
<table>
    <thead>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </thead>
    <#list stus as stu>
        <tr>
            <#--使用 _index 来遍历的序号-->
            <td>${stu_index + 1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.money}</td>
        </tr>
    </#list>
</table>

</body>
</html>

效果

LIST输出效果

遍历Map数据

1、数据模型

在controller 内使用map指令遍历数据模型中的 stuMap。

//准备map数据
HashMap<Object, Object> stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
//像数据模型内放数据
map.put("stu1",stu1);
//向数据模型放入stuMap
map.put("stuMap", stuMap);

2、模板

通过Map输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>

通过Map输出stu1的学生信息:<br/>
姓名:${stuMap.stu1.name}<br/>
年龄:${stuMap.stu1.age}<br/>


<p>使用map指令遍历数据模型中的stuMap。</p>
<table>
    <thead>
    <td>序号</td>
    <td>姓名</td>
    <td>年龄</td>
    <td>钱包</td>
    </thead>
    <#list stuMap?keys as k>
        <tr>
            <td>${k_index + 1}</td>
            <td>${stuMap[k].name}</td>
            <td>${stuMap[k].age}</td>
            <td>${stuMap[k].money}</td>
        </tr>
    </#list>
</table>

3、输出

map遍历效果

if指令

if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。

  1. 数据模型:

    使用list指令中测试数据模型。

  2. 模板

    <table>
        <thead>
            <td>序号</td>
            <td>姓名</td>
            <td>年龄</td>
            <td>钱包</td>
        </thead>
        <#list stus as stu>
            <tr>
                <#--使用 _index 来遍历的序号-->
                <td>${stu_index + 1}</td>
                <td <#if stu.name == '小明'>style="background: red" </#if>>${stu.name}</td>
                <td>${stu.age}</td>
                <td>${stu.money}</td>
            </tr>
        </#list>
    </table>
    

    通过阅读上边的代码,实现的功能是:如果姓名为 “小明” 则背景色显示为红色。

  3. 输出

    通过测试发现 姓名为小明的背景色为红色。

三元运算
${ (type == 'SPOT') ?string('热点词','热点搜索')}  

2、其他指令

运算符
  1. 算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %

  2. 逻辑运算符 逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误

  3. 比较运算符 表达式中支持的比较运算符有如下几个: 1 =或者==:判断两个值是否相等. 2 !=:判断两个值是否不等. 3 >
    或者gt:判断左边值是否大于右边值

  4. >= 或者 gte: 判断左边值是否大于等于右边值

  5. < 或者 lt: 判断左边值是否小于右边值

  6. <= 或者 lte: 判断左边值是否小于等于右边值,注意: = 和 != 可以用于字符

串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误

而且 FreeMarker 是精确比较, "x","x ","X" 是不等的.其它的运行符可以作用

于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替 > 会有更好的效果,因为 FreeMarker 会把 > 解释成 FTL 标签的结束字符, 当然,也可以使 括号 来避免这种情况, 如:<#if (x>y)>

空值处理
  1. 判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false

    例:为防止stus为空报错可以加上判断如下:

    <#if stus??>
    <#list stus as stu>
    ......
    </#list>
    </#if>
    
  2. 缺失变量默认值使用 “!” 使用 ! 要以指定一个默认值,当变量为空时显示默认值。例如: ${name!''} 表示如果 name 为空显示空字符串。

    如果是嵌套对象则建议使用()括起来。

    例: ${(stu.bestFriend.name)!’’}表示,如果stu或bestFriend或name为空默认显示空字符串。

内建函数

内建函数语法格式: 变量+?+函数名称

  1. 得到某个集合的大小:${集合名?size}

  2. 日期格式化

    显示年月日: ${today?date}
    显示时分秒:${today?time}
    显示日期+时间:${today?datetime} <br>
    自定义格式化: ${today?string("yyyy年MM月")}
    
  3. 内建函数 c

    map.put("point", 102920122);

    point 是数字型,使用 ${point} 会显示这个数字的值,并且每三位使用逗号分隔。例如:“102,920,122”

    如果不想显示为每三位分隔的数字,可以使用 c 函数将数字型转成字符串输出

    例如:${point?c}

  4. 将json字符串转成对象

    一个例子:

    其中用到了 assign标签,assign的作用是定义一个变量。

    <#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
    <#assign data=text?eval />
    开户行:${data.bank} 账号:${data.account}
    
完整的模板
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf‐8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
<br/>
<table>
    北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090<tr>
    <td>序号</td>
    <td>姓名</td>
    <td>年龄</td>
    <td>钱包</td>
</tr>
<#list stus as stu>
<tr>
    <td>${stu_index + 1}</td>
    <td <#if stu.name =='小明'>style="background:red;"</#if>>${stu.name}</td>
    <td>${stu.age}</td>
    <td >${stu.money}</td>
</tr>
</#list>
</table>
<br/><br/>
输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
输出stu1的学生信息:<br/>
姓名:${stu1.name}<br/>
年龄:${stu1.age}<br/>
遍历输出两个学生信息:<br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
<#list stuMap?keys as k>
<tr>
    <td>${k_index + 1}</td>
    <td>${stuMap[k].name}</td>
    <td>${stuMap[k].age}</td>
    <td >${stuMap[k].money}</td>
</tr>
</#list>
</table>
</br>
<table>
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>出生日期</td>
        <td>钱包</td>
        <td>最好的朋友</td>
        <td>朋友个数</td>
        <td>朋友列表</td>
    </tr>
<#if stus??>
    <#list stus as stu>
<tr>
    <td>${stu.name!''}</td>
    <td>${stu.age}</td>
    <td>${(stu.birthday?date)!''}</td>
    <td>${stu.money}</td>
    <td>${(stu.bestFriend.name)!''}</td>
    <td>${(stu.friends?size)!0}</td>
    <td>
<#if stu.friends??>
<#list stu.friends as firend>
    ${firend.name!''}<br/>
</#list>
</#if>
    </td>
</tr>
    </#list>
</#if>
</table>
<br/>
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}
</body>
</html>

3、静态化测试

在cms中使用freemarker将页面生成html文件,本节测试html文件生成的方法:

1、使用模板文件静态化

定义模板文件,使用freemarker静态化程序生成html文件。

2、使用模板字符串静态化

定义模板字符串,使用freemarker静态化程序生成html文件。

使用模板文件静态化

在 test 下创建测试类,并且将main下的 resource/templates 拷贝到 test 下,本次测试使用之前我们在 main 下创建的模板文件,并创建测试类。 目录结构如下

创建测试类

//基于模板生成静态文件
@Test
public void testGenerateHtml() throws IOException, TemplateException {
    //创建配置类
    Configuration configuration = new Configuration(Configuration.getVersion());
    //设置模板路径
    String classPath = this.getClass().getResource("/").getPath();
    classPath = java.net.URLDecoder.decode(classPath,"utf-8"); //路径包含中文需要进行转码
    configuration.setDirectoryForTemplateLoading(new File(classPath +"/templates"));
    //设置字符集
    configuration.setDefaultEncoding("utf-8");
    //加载模板
    Template template = configuration.getTemplate("test1.ftl");
    //数据模型
    Map map = getMap();
    //静态化
    String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
    //静态化内容
    InputStream inputStream = IOUtils.toInputStream(content);
    //输出到文件
    FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html"));
    int copy = IOUtils.copy(inputStream, fileOutputStream);
}

public Map getMap(){
    Map<String,Object> map = new HashMap<>();
    map.put("name", "黑马程序员");
    //学生对象1
    Student stu1 = new Student();
    stu1.setName("小明");
    stu1.setAge(18);
    stu1.setMoney(1888.123f);
    stu1.setBirthday(new Date());
    //学生对象2
    Student stu2 = new Student();
    stu2.setName("小红");
    stu2.setAge(22);
    stu2.setMoney(1888.123f);
    stu2.setBirthday(new Date());
    //创建一个List对象,用于储存上面这两个学生对象
    ArrayList<Object> stus = new ArrayList<>();
    stus.add(stu1);
    stus.add(stu2);
    //向数据模型中放入List
    map.put("stus",stus);
    //准备map数据
    HashMap<Object, Object> stuMap = new HashMap<>();
    stuMap.put("stu1",stu1);
    stuMap.put("stu2",stu2);
    //像数据模型内放数据
    map.put("stu1",stu1);
    //向数据模型放入stuMap
    map.put("stuMap", stuMap);
    return map;
}

运行测试,得到html模板

使用模板字符串静态化

详细代码省略,使用字符串和使用模板的区别是 这里直接定义一个 HTML 的字符串变量,例如

String templateString="" +
    "<html>\n" +
    " <head></head>\n" +
    " <body>\n" +
    " 名称:${name}\n" +
    " </body>\n" +
    "</html>";

而前面的例子是从ftl模板中获取内容

//加载模板
Template template = configuration.getTemplate("test1.ftl");

0x04 章节总结

本章节主要介绍了 Freemarker 模板引擎的语法介绍和基本的应用,并了解了 Freemarker 在进行模板渲染时的API调用过程。

三、页面静态化实战

0x01 页面静态化流程

通过上边对 FreeMarker 的研究我们得出:模板 + 数据模型 = 输出,页面静态化需要准备数据模型和模板,先知道数据模型的结构才可以编写模板,因为在模板中要引用数据模型中的数据,本节将系统讲解 CMS 页面数据模型获取、模板管理及静态化的过程。

下边我们讨论一个问题:如何获取页面的数据模型?

CMS 管理了各种页面,CMS 对页面进行静态化时需要数据模型,但是 CMS 并不知道每个页面的数据模型的具体内容,它只管执行静态化程序便可对页面进行静态化,所以 CMS 静态化程序需要通过一种通用的方法来获取数据模型。

在编辑页面信息时指定一个 DataUrl ,此 DataUrl 便是获取数据模型的 Url,它基于 Http方式,CMS 对页面进行静态化时会从页面信息中读取 DataUrl,通过 Http 远程调用的方法请求 DataUrl 获取数据模型 。

管理员怎么知道 DataUrl 的内容呢?

举例说明:

此页面是轮播图页面,它的 DataUrl 由开发轮播图管理的程序员提供。

此页面是精品课程推荐页面,它的 DataUrl 由精品课程推荐的程序员提供。

此页面是课程详情页面,它的 DataUrl 由课程管理的程序员提供。

页面静态化流程如下图:

1、静态化程序首先读取页面获取DataUrl。

2、静态化程序远程请求DataUrl得到数据模型。

3、获取页面模板。

4、执行页面静态化

0x02 数据模型

1、轮播图DataUrl接口

需求分析

CMS 中有轮播图管理、精品课程推荐的功能,以轮播图管理为例说明:轮播图管理是通过可视化的操作界面由管理员指定轮播图图片地址,最后将轮播图图片地址保存在 cms_config 集合中,下边是轮播图数据模型

针对首页的轮播图信息、精品推荐等信息的获取统一提供一个 Url 供静态化程序调用,这样我们就知道了轮播图页面、精品课程推荐页面的 DataUrl,管理在页面配置中将此 Url 配置在页面信息中。本小节开发一个查询轮播图、精品推荐信息的接口,此接口供静态化程序调用获取数据模型 。

接口定义

轮播图信息、精品推荐等信息存储在MongoDB的cms_config集合中。

cms_config有固定的数据结构,如下:

package com.xuecheng.framework.domain.cms;

import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

/**
 * Created by admin on 2018/2/6.
 */
@Data
@ToString
@Document(collection = "cms_config")
public class CmsConfig {
    @Id
    private String id; //主键
    private String name; //数据模型名称
    private List<CmsConfigModel> model;  //数据模型项目

}

数据类型项目的内容如下:

package com.xuecheng.framework.domain.cms;

import lombok.Data;
import lombok.ToString;

import java.util.Map;

/**
 * Created by admin on 2018/2/6.
 */

@Data
@ToString
public class CmsConfigModel {
    private String key;  //主键
    private String name; //项目名称
    private String url;  //项目url
    private Map mapValue;  //项目复杂值
    private String value;  //项目简单值
}

上边的数据结构可以对照 cms_config 的数据进行分析。

其中,在 mapValue 中可以储存一些复杂的数据的模型内容。

根据配置信息Id查询配置信息,定义接口如下:

package com.xuecheng.api.cms;

import com.xuecheng.framework.domain.cms.CmsConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@Api(value="cms配置管理接口",description = "cms配置管理接口,提供数据模型的管理、查询接口")
public interface CmsConfigControllerApi {

    @ApiOperation("根据id查询CMS配置信息")
    public CmsConfig getModel(String id);
}
Dao层
package com.xuecheng.manage_cms.dao;
import com.xuecheng.framework.domain.cms.CmsConfig;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> {
}
Service层
package com.xuecheng.manage_cms.service;

import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.manage_cms.dao.CmsConfigRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class CmsConfigService {
    @Autowired
    CmsConfigRepository cmsConfigRepository;

    //根据id查询配置管理信息
    public CmsConfig getConfigById(String id){
        Optional optional = cmsConfigRepository.findById(id);
        if(optional.isPresent()){
            CmsConfig cmsConfig = (CmsConfig) optional.get();
            return cmsConfig;
        }
        return null;
    }
}
Controller层
package com.xuecheng.manage_cms.controller;

import com.xuecheng.api.cms.CmsConfigControllerApi;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.manage_cms.service.CmsConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {
    @Autowired
    CmsConfigService cmsConfigService;

    @GetMapping("/getmodel/{id}")
    @Override
    public CmsConfig getModel(@PathVariable("id") String id) {
        CmsConfig configById = cmsConfigService.getConfigById(id);
        return configById;
    }
}
测试

使用postman测试接口:

get请求:http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f (轮播图信息)

2、远程请求接口

SpringMVC 提供 RestTemplate 请求 http 接口,RestTemplate 的底层可以使用第三方的 http 客户端工具实现 http 的请求,常用的 http 客户端工具有 Apache HttpClient、OkHttpClient 等,本项目使用 OkHttpClient 完成 http 请求,原因也是因为它的性能比较出众。

添加依赖
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
</dependency
配置 RestTemplate

在SpringBoot启动类中配置 RestTemplate

...
public class ManageCmsApplication {
    public static void main(String[] args) {
        SpringApplication.run(ManageCmsApplication.class,args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }
}
3、测试 RestTemplate
@SpringBootTest
@RunWith(SpringRunner.class)
public class RestTemplateTest {
    @Autowired
    RestTemplate restTemplate;

    @Test
    public void testRestTemplate(){
        ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f", Map.class);
        System.out.println(forEntity);
    }
}

运行测试

0x03 模板管理

1、模板管理业务流程

CMS提供模板管理功能,业务流程如下:

1、要增加新模板首先需要制作模板,模板的内容就是 Freemarker ftl 模板内容。

2、通过模板管理模块功能新增模板、修改模板、删除模板。

3、模板信息存储在 MongoDB 数据库,其中模板信息存储在 cms_template 集合中,模板文件存储在 GridFS 文件系统中。

下边是一个模板的例子:

{
    "_id" : ObjectId("5a962b52b00ffc514038faf7"),
    "_class" : "com.xuecheng.framework.domain.cms.CmsTemplate",
    "siteId" : "5a751fab6abb5044e0d19ea1",
    "templateName" : "首页",
    "templateParameter" : "",
    "templateFileId" : "5a962b52b00ffc514038faf5"
}

上边模板信息中 templateFileId 是模板文件的ID,此ID对应 GridFS 文件系统中文件ID。

2、模板制作

编写模板文件

轮播图页面原型

在门户的静态工程目录有轮播图的静态页面,路径是:/include/index_banner.html。

数据模型为:

通过http 获取到数据模型如下:

下图数据模型的图片路径改成可以浏览的正确路径。

{ 
    "_id" : ObjectId("5a791725dd573c3574ee333f"), 
    "_class" : "com.xuecheng.framework.domain.cms.CmsConfig", 
    "name" : "轮播图", 
    "model" : [
        {
            "key" : "banner1", 
            "name" : "轮播图1地址", 
            "value" : "http://www.xuecheng.com/img/widget-bannerA.jpg"
        }, 
        {
            "key" : "banner2", 
            "name" : "轮播图2地址", 
            "value" : "http://www.xuecheng.com/img/widget-bannerB.jpg"
        }
    ]
}

编写模板

在freemarker测试工程中新建模板 index_banner.ftl。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="http://www.xuecheng.com/plugins/normalize-css/normalize.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/plugins/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/css/page-learing-index.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/css/page-header.css" />
</head>
<body>
<div class="banner-roll">
    <div class="banner-item">
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div>-->
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div>-->
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div>-->
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div>-->
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div>-->
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div>-->
        <#--渲染轮播图-->
        <#if model??>
            <#list model as item>
                <div class="item" style="background-image: url(${item.value});"></div>
            </#list>
        </#if>
    </div>
    <div class="indicators"></div>
</div>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript">
    var tg = $('.banner-item .item');
    var num = 0;
    for (i = 0; i < tg.length; i++) {
        $('.indicators').append('<span></span>');
        $('.indicators').find('span').eq(num).addClass('active');
    }

    function roll() {
        tg.eq(num).animate({
            'opacity': '1',
            'z-index': num
        }, 1000).siblings().animate({
            'opacity': '0',
            'z-index': 0
        }, 1000);
        $('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active');
        if (num >= tg.length - 1) {
            num = 0;
        } else {
            num++;
        }
    }
    $('.indicators').find('span').click(function() {
        num = $(this).index();
        roll();
    });
    var timer = setInterval(roll, 3000);
    $('.banner-item').mouseover(function() {
        clearInterval(timer)
    });
    $('.banner-item').mouseout(function() {
        timer = setInterval(roll, 3000)
    });
</script>
</body>
</html>
模板测试

在 freemarker 测试工程的入口文件注册 RestTemplate 的Bean

原讲义只在 cms工程下注册了bean,记得 freemarker 测试工程下也要注册

@SpringBootApplication
public class FreemarkerTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(FreemarkerTestApplication.class,args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }
}

在 freemarker 测试工程的 FreemarkerController 下编写一个方法测试轮播图模板,代码如下:

@Autowired
RestTemplate restTemplate;

/**
 * 用于调试banner模板
 */
@RequestMapping("/banner")
public String indexBanner(Map<String,Object> map){
    String dataUrl = "http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f";
    ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
    Map body = forEntity.getBody();
    map.putAll(body); //将获取到的数据赋值给map,并渲染至模板内
    return "index-banner";
}

请求:http://localhost:8088/freemarker/banner

3、GridFS 研究

GridFS介绍

GridFS 是 MongoDB 提供的用于持久化存储文件的模块,CMS使用MongoDB存储数据,使用GridFS 可以快速集成开发。

它的工作原理是:

在 GridFS 存储文件是将文件分块存储,文件会按照256KB的大小分割成多个块进行存储,GridFS 使用两个集合(collection)存储文件,一个集合是 chunks, 用于存储文件的二进制数据;一个集合是 files,用于存储文件的元数据信息(文件名称、块大小、上传时间等信息)。

从 GridFS 中读取文件要对文件的各各块进行组装、合并。
详细参考:https://docs.mongodb.com/manual/core/gridfs/

GridFS 存取文件测试

1、存文件

这里我们在cms工程内进行测试

使用 GridFsTemplate 存储文件测试代码:
向测试程序注入 GridFsTemplate 。

@SpringBootTest
@RunWith(SpringRunner.class)
public class GridFsTest {

    @Autowired
    GridFsTemplate gridFsTemplate;

    @Test
    public void testGridFs() throws FileNotFoundException {
        //要储存的文件
        File file = new File("d:/resources/index-banner.ftl");
        //定义输入流
        FileInputStream fileInputStream = new FileInputStream(file);
        //向GridFS存储文件
        ObjectId objectId = gridFsTemplate.store(fileInputStream, "index-banner");
        //得到文件ID
        String fileId = objectId.toString();
        System.out.println(fileId);

    }
}

储存结果

存储原理说明:

文件存储成功得到一个文件 id
此文件 id 是 fs.files 集合中的主键。
可以通过文件 id 查询 fs.chunks 表中的记录,得到文件的内容。

2、读取文件

1)在config包中定义 Mongodb 的配置类,如下:

GridFSBucket用于打开下载流对象

@Configuration
public class MongoConfig {
    @Value("${spring.data.mongodb.database}")
    String db;

    @Bean
    public GridFSBucket getGridFSBucket(MongoClient mongoClient){
        MongoDatabase database = mongoClient.getDatabase(db);
        GridFSBucket gridFSBucket = GridFSBuckets.create(database);
        return gridFSBucket;
    }
}

2)测试代码如下

@Autowired
GridFSBucket gridFSBucket;

//取文件
@Test
public void queryFile() throws IOException {
    String fileId = "5e79e4323304a26aecec55cd";
    //根据ID查询文件
    GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
    //打开下载流对象
    GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
    //创建gridFsResource,用于获取流对象
    GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
    //获取流中的数据
    String s = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
    System.out.println(s);
}
删除文件
//删除文件
@Test
public void testDelFile() throws IOException {
//根据文件id删除fs.files和fs.chunks中的记录
gridFsTemplate.delete(Query.query(Criteria.where("_id").is("5b32480ed3a022164c4d2f92")));
}

4、模板储存

根据模板管理的流程,最终将模板信息存储到 MongoDB 的 cms_template 中,将模板文件存储到 GridFS中。模板管理功能在课堂中不再讲解,教学中手动向 cms_template 及 GridFS 中存储模板,方法如下:

1)添加模板

使用 GridFS 测试代码存储模板文件到 GridFS,并得到文件模板 id,向 cms_template 添加记录

2)删除模板

使用 GridFS 测试代码根据文件id删除模板文件。

根据模板id删除 cms_template 中的记录。

3)修改模板信息

使用Studio 3T修改 cms_template 中的记录。

4)修改模板文件

1)通过Studio 3T修改模板文件(此方法限文件小于256K)可以通过Studio 3T修改模板文件,先找到模板文件,再导入进去:

0x04 静态化测试

1、填写页面 DataUrl

修改页面管理模板代码,实现编辑页面DataUrl。
注意:此地址由程序员提供给系统管理员,由系统管理员录入到系统中。

修改页面管理前端的 page_edit.vue

在表单中添加 dataUrl 输入框:

<el-form-item label="数据Url" prop="dataUrl">
  <el-input v-model="pageForm.dataUrl" auto-complete="off" ></el-input>
</el-form-item>

fromData 表单内添加 dataUrl 字段

修改页面管理服务端 PageService

在 PageService.updateCmsPage 更新 cmsPage 数据代码中添加:

//更新dataUrl
one.setDataUrl(cmsPage.getDataUrl());

新增页面也重复上面的操作,新增 dataUrl 的表单

2、静态化程序

在 model.response.cmscode 下新增一些错误代码

CMS_PAGE_NOT_EXISTS(false,24002,"页面不存在!"),
CMS_GENRATEHTML_DATAURL_IS_NULL(false,24003,"从页面信息中找不到获取数据的url"),
CMS_GENRATEHTML_DATA_IS_NULL(false,24004,"根据页面的数据url获取不到数据"),
CMS_GENRATEHTML_TEMPLATE_IS_NULL(false,24005,"页面模板为空"),
CMS_GENRATEHTML_HTML_IS_NULL(false,24006,"生成的静态html为空"),
CMS_GENRATEHTML_SAVE_HTML_ERROR(false,24007,"保存静态html出错"),
CMS_GENRATEHTML_PERVIEW_IS_NULL(false,24008,"预览页面为空");
获取数据模型

静态化程序远程请求DataUrl得到数据模型

@Autowired
RestTemplate restTemplate;

//从dataUrl中获取页面模型数据
private Map getModelByPageId(String pageId){
    //查询页面信息
    CmsPageResult cmsPageResult = this.cmsPageQueryById(pageId);
    CmsPage cmsPage = cmsPageResult.getCmsPage();
    //页面不存在
    if(cmsPage == null){
        ExceptionCast.cast(CmsCode.CMS_PAGE_NOT_EXISTS);
    }
    //取出dataUrl
    String dataUrl = cmsPage.getDataUrl();
    if(StringUtils.isEmpty(dataUrl)){
        ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_DATAURL_IS_NULL);
    }
    //发送请求获取模型数据
    ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl,Map.class);
    Map body = forEntity.getBody();
    return body;
}

获取页面模板
@Autowired
CmsTemplateRepository templateRepository;

@Autowired
GridFsTemplate gridFsTemplate;

@Autowired
GridFSBucket gridFSBucket;

//获取页面模板数据
private String getTemplateByPageId(String pageId){
    //查询页面信息
    CmsPageResult cmsPageResult = this.cmsPageQueryById(pageId);
    CmsPage cmsPage = cmsPageResult.getCmsPage();
    //页面不存在
    if(cmsPage == null){
        ExceptionCast.cast(CmsCode.CMS_PAGE_NOT_EXISTS);
    }
    //获取页面模板
    String templateId = cmsPage.getTemplateId();
    if(StringUtils.isEmpty(templateId)){
        ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_TEMPLATE_IS_NULL);
    }
    Optional<CmsTemplate> optional = templateRepository.findById(templateId);
    if(optional.isPresent()){
        CmsTemplate cmsTemplate = optional.get();
        //获取模板文件id
        String templateFileId = cmsTemplate.getTemplateFileId();
        //取出模板文件内容
        GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
        //打开下载流对象
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
        //创建GridResource
        GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);

        try {
            String content = IOUtils.toString(gridFsResource.getInputStream(), "utf‐8");
            return content;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}
执行页面静态化
/**
     * 页面静态化
     * @param pageId
     * @return
     * @throws IOException
     * @throws TemplateException
     */
public String getPageHtml(String pageId) throws IOException, TemplateException {
    //获取页面模型数据
    Map modelByPageId = this.getModelByPageId(pageId);
    if(modelByPageId == null){
        //获取页面模型数据为空
        ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_DATA_IS_NULL);
    }
    //获取页面模板
    String templateContent = getTemplateByPageId(pageId);
    if(StringUtils.isEmpty(templateContent)){
        //页面模板为空
        ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_TEMPLATE_IS_NULL);
    }
    //获取页面静态化数据
    String html = generateHtml(templateContent, modelByPageId);
    if(StringUtils.isEmpty(html)){
        ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_HTML_IS_NULL);
    }
    return html;
}

//构建静态化页面数据
private String generateHtml(String template,Map model) throws IOException, TemplateException {
    //生成配置类
    Configuration configuration = new Configuration(Configuration.getVersion());
    //模板加载器
    StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
    stringTemplateLoader.putTemplate("template",template);
    //配置模板加载器
    configuration.setTemplateLoader(stringTemplateLoader);

    //获取模板
    Template template1 = configuration.getTemplate("template");
    String html = FreeMarkerTemplateUtils.processTemplateIntoString(template1, model);
    return html;
}

调用逻辑

创建测试的页面

原讲义中该步骤是省略的

为了更清晰的了解页面静态化的逻辑,我们新建一个页面来进行测试;

在GridFS 的章节中,我们在单元测试中将 index-banner.ftl 文件的内容储存到了 GridFS 得到一个 fileId ,我这里的是 5e7a20fc3304a25230861f4c ,我们用这个fileId 在 mongoDB 的 cms_tempalte 集合中手动创建一个页面模板,创建成功后我们得到如下的数据

{ 
    "_id" : ObjectId("5e7a2212b1afec2187ed89ac"), 
    "_class" : "com.xuecheng.framework.domain.cms.CmsTemplate", 
    "siteId" : "5a751fab6abb5044e0d19ea1", 
    "templateName" : "轮播图_测试", 
    "templateParameter" : "", 
    "templateFileId" : "5e7a20fc3304a25230861f4c"
}

然后进入到我们之前构建的新增页面选项,添加一个新的页面;

填入 dataUrl http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f

得到该页面的id为 5e7a251a3304a252280804dc

单元测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestPageService {
    @Autowired
    PageService pageService;

    @Test
    public void testGetPageHtml() throws IOException, TemplateException {
        String pageHtml = pageService.getPageHtml("5e7a251a3304a252280804dc");
        System.out.println(pageHtml);
    }
}

四、页面预览

1、页面预览开发

需求分析

页面在发布前增加页面预览的步骤,方便用户检查页面内容是否正确。页面预览的流程如下:

1、用户进入 cms 前端,点击 “页面预览” 在浏览器请求 cms 页面预览链接。

2、cms 根据页面 id 查询DataUrl并远程请求DataUrl获取数据模型。

3、cms 根据页面id查询页面模板内容

4、cms 执行页面静态化。

搭建环境

在cms服务需要集成freemarker:

1、在CMS服务中加入freemarker的依赖

<!--freemarker-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

2、在application.yml配置freemarker

server:
  port: 31001
spring:
  application:
    name: xc-service-manage-cms
  data:
    mongodb:
      uri: mongodb://root:123123@localhost:27017
      database: xc_cms
  freemarker:
    cache: false #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0

Service

静态化方法在静态化测试章节已经实现

Controller

调用 service 的静态化方法,将静态化内容通过 response 输出到浏览器显示

创建 CmsPagePreviewController 类,用于页面预览:

请求页面id,查询得到页面的模板信息、数据模型 url,根据模板和数据生成静态化内容,并输出到浏览器。

@Controller
public class CmsPagePreviewController  extends BaseController {
    @Autowired
    PageService pageService;

    //接收到页面id
    @RequestMapping(value = "/cms/preview/{pageId}", method = RequestMethod.GET)
    public void preView(@PathVariable("pageId") String pageId) throws IOException, TemplateException {
        String pageHtml = pageService.getPageHtml(pageId);
        if(StringUtils.isNotEmpty(pageHtml)){
            try {
                //通过response对象将页面内容输出
                ServletOutputStream outputStream = response.getOutputStream();
                outputStream.write(pageHtml.getBytes("utf-8"));
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

2、页面预览测试

配置nginx代理

这里说一下我们为什么要通过nginx代理来配置页面预览,因为在实际生产环境中,我们的cms节点可能会有多个,需要从nginx进行反向代理,实现负载均衡的等需求。

为了通过 nginx 请求静态资源(css、图片等),通过 nginx 代理进行页面预览。

在www.xuecheng.com虚拟主机配置:

#页面预览
location /cms/preview/ {
	proxy_pass http://cms_server_pool/cms/preview/;
}

配置 cms_server_pool 将请求转发到 cms:

#cms页面预览
upstream cms_server_pool{
	server 127.0.0.1:31001 weight=10;
}

全部配置

#cms页面预览
upstream cms_server_pool{
	server 127.0.0.1:31001 weight=10;
}
server{
	listen       80;
	server_name  www.xuecheng.com;
	ssi on;
	ssi_silent_errors on;
	location / {
		alias  C:/Users/root/Desktop/笔记/demos/JavaEE/微服务/Project/XueChengOnline/xcEduUI01/xc-ui-pc-static-portal/;
		index  index.html;
	}
	#页面预览
	location /cms/preview/ {
		proxy_pass http://cms_server_pool/cms/preview/;
	}
}

重新加载nginx 配置文件。
从cms_page找一个页面进行测试。注意:页面配置一定要正确,需设置正确的模板 id 和dataUrl。

在浏览器打开:http://www.xuecheng.com/cms/preview/5e7a251a3304a252280804dc

5e7a251a3304a252280804dc:轮播图页面的id (根据自己实际用于测试的页面ID)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQgLDFMw-1595564553549)(https://qnoss.codeyee.com/20200704_xuechengday04/image24)]

添加 “页面预览” 链接

添加预览按钮

 <el-table-column label="操作" width="230" fixed="right">
    <template slot-scope="scope">
        <el-button
        size="mini"
        type="success"
        @click="preview(scope.row.pageId)">预览
        </el-button>
    </template>
</el-table-column>

预览函数

//页面预览
preview(pageId){
  window.open("http://www.xuecheng.com/cms/preview/"+pageId)
},

测试

😁 认识作者

作者:👦 LCyee ,全干型代码🐕

自建博客:https://www.codeyee.com

记录学习以及项目开发过程中的笔记与心得,记录认知迭代的过程,分享想法与观点。

优快云 博客:https://blog.youkuaiyun.com/codeyee

记录和分享一些开发过程中遇到的问题以及解决的思路。

欢迎加入微服务练习生的队伍,一起交流项目学习过程中的一些问题、分享学习心得等,不定期组织一起刷题、刷项目,共同见证成长。

第八届传智杯WPS智能办公能力挑战赛练习题库 本文是关于WPS Office软件操作知识的选择题汇总,内容涵盖WPS文字、表格和演示三大组件的常用功能。主要包括:云文档协同编辑、文本框插入、幻灯片打印设置、数据排序与筛选、函数应用(如SUMIF、COUNT)、动画效果设置、格式刷使用、页眉页脚设置、单元格引用类型等基础操作要点。同时涉及计算机基础概念如二进制、ROM存储器等。题目以单选和多选形式呈现,附带详细答案解析,全面检验用户对WPS办公软件的功能掌握程度和实际操作能力,适用于办公软件技能培训和水平测试。 阅读详情

相关推荐

Windows环境Oracle19C安装与配置全攻略

本文详细介绍了在Windows环境下安装与配置Oracle19C数据库的完整流程,包括环境准备、安装步骤、网络配置、客户端工具设置及常见问题解决方案。特别针对Windows 10和Windows Server 2016及以上版本提供了优化建议,帮助用户高效完成Oracle19C的部署与维护。

weixin_28455801的博客 487

微服务项目中 页面静态化的分析

一.页面静态化分析 涉及技术:fastdfs、velocity、redis、rabbitMQ、mysql、nginx… 1.页面静态化分析 1.1.什么是页面静态化 ​ 传统的页面的渲染方式是,请求某个页面的时候,后台需要查询数据 , 然后视图解析器会找到对应的页面的模板进行视图的渲染 ,然后写到浏览器 , 而页面静态化就是把前面的这个动作提前做好, 也就是说事先就把这个页面的内容渲染好,生成h...

马尾与发圈 820

人工智能——框架表示法

1975年美国明斯基在“A framework for representing knowledge”提出了框架理论。 1. 框架及框架的一般表示形式 框架:一种描述对象属性的数据结构。 框架的一般表示形式 <框架名> 槽名1: 侧面名1:值1,值2…,值p1 侧面名2:值1,值2…,值p2 侧面名2:值1,值2…,值pm1 槽名2: 侧面名...

weixin_33759269的博客 1642

微服务架构(9):Thymeleaf页面静态化

微服务架构(9):Thymeleaf和页面静态化0.学习目标1.商品详情1.1.Thymeleaf1.2.商品详情页服务1.2.1.创建module1.2.2.pom依赖1.2.3.编写启动类1.2.4.application.yml文件1.2.5.页面模板1.3.页面跳转1.3.1.修改页面跳转路径1.3.2.nginx反向代理1.3.3.编写跳转controller1.3.4.测试1.4.封装...

rnang0的博客 615

家庭宽带IPv6避坑指南:如何通过DHCPv6-PD获取公网地址(附OpenWRT配置)

本文详细解析了家庭宽带IPv6的地址分配机制,重点介绍了通过DHCPv6-PD获取公网地址的方法,并提供了OpenWRT路由器的具体配置步骤。从路由器通告(RA)标记位到前缀委派(PD)流程,帮助用户解决IPv6部署中的常见问题,实现稳定高效的IPv6网络连接。

backprop5master的博客 1196

四旋翼黑科技揭秘:如何利用PX4隐藏的角加速度数据提升控制性能

本文深入探讨了如何利用PX4飞控中隐藏的角加速度数据,结合增量非线性动态逆(INDI)控制方法,显著提升四旋翼无人机的控制性能。通过实战教程,指导开发者从数据订阅、INDI原理到代码实现,构建响应更快、鲁棒性更强的控制回路,有效应对突风与快速机动。

lake5的博客 720

springcloud微服务_05_redis优化和页面静态化

前言 商品模块博客中的商品类型进行优化。主要是使用Redis进行商品类型后台缓存优化和模板Velocity进行商品类型主页面页面静态化 一、商品类型优化方案 1、为什么要进行优化 商品的品牌和类型,都是从数据库中全部获取出来的,每一次都要去数据库中查询一次,从而增加数据库的访问压力,反应时间就会长,造成用户体验差。 后台管理(Redis缓存解决) 使用缓存解决对数据库的频繁操作问题,用内存...

Abdullahi_kanye的博客 1258

Android逆向实战:通过smali/baksmali动态注入DEX逻辑

本文详细介绍了在无源码条件下对Android应用进行动态逻辑注入的实战方法。通过使用smali和baksmali工具对DEX文件进行反编译、修改与重打包,实现在目标方法中插入自定义代码(如日志打印),并分享了定位目标、处理寄存器、避免常见错误的实用技巧与避坑指南。

weixin_29008691的博客 364

微服务[学成在线] day20:项目总结

???? 写在前面 我为什么要花时间去写这个笔记? 由于在学习该项目的过程中发现了原讲义存在的一些问题,所以该笔记基于「学成在线」微服务项目 PDF 讲义进行编写,并且投入了时间去优化了笔记的格式、代码的高亮、重点的标记等。 以及一些原讲义中所描述的一些知识点使我无法理解的内容,我会对这些内容的表达方式进行修改或者提出一些问题,并且用我自己所理解的一些想法去重新的解释这个问题。 总结一下原 PDF 讲义中已知的一些问题: 从 PDF 中复制出来的代码,部分特殊符号的编码有问题,并且不易被发现,例如横杠

通往体面生活的路上 3261

无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程

当您遇到类似于"无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程"的错误消息时,请按照上述步骤来解决问题。当您尝试加载文件C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1时,可能会遇到类似于"无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程"的错误消息。请注意,更改执行策略可能会带来一些安全风险。

PixelInk的博客 1921

【电源专题】Type-C母座外壳直接接主板GND还是接RC呢?别再纠结了!

Type-C屏蔽壳接地这件事,没有唯一标准答案。符合规范、贴合产品、能过认证,就是最优解。希望这篇文章能帮你理清思路。

一点硬件 601

阶段5 3.微服务项目【学成在线】_day04 页面静态化_21-页面静态化-静态化测试-静态化程序编写...

定义getModelByPageId 定义一个专门的代码,用来表示页面找不到 通过restTemplate方法获取dataUrl接口的数据 调用获取Map数据 获取页面模板 创建cmsTemplate的Dao 在Service注入进来。 拿到模板的id 直接复制刚才test内的代码 需要注入...

weixin_30566063的博客 210

rk3576 点亮 LCD(eDP)

RK3576 处理器集成了对 **eDP (Embedded DisplayPort)** 接口的支持,主要用于驱动嵌入式或笔记本电脑的内置屏幕。支持 **eDP 1.3 版本协议**, 最大可支持 ****4K@120Hz**。

weixin_35723192的博客 354

微服务[学成在线] day09:Eureka、Feign、课程预览实现

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day09 的内容 构建基于 Eureka 的服务注册中心 Ribbon 的基本使用,以及使用 Ribbon 来进行服务间的负载均衡调用 使用 Feign 实现服务之间的远程调用 课程预览功能开发 目录 文章目录???? 知识点概览目录一、Eureka 注册中心0x01 需求分析0

通往体面生活的路上 568

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

微服务[学成在线] day06:页面发布以及课程管理

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day06 的内容 使用 Spring boot 集成 RabbitMQ 和 GridFS 实现基于生产者和消费者模型的页面静态化发布的流程。 在本章节的知识点中,再次复习了基于 GridFS 和 RabbitMQ 的分布式静态页面发布的知识点,深化了记忆。 使用三级菜单实现课程计

通往体面生活的路上 627

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

阶段5 3.微服务项目【学成在线】_day04 页面静态化_23-页面预览-页面预览开发

1、用户进入cms前端,点击“页面预览”在浏览器请求cms页面预览链接。 2、cms根据页面id查询DataUrl并远程请求DataUrl获取数据模型。 3、cms根据页面id查询页面模板内容 4、cms执行页面静态化。 5、cms将静态化内容响应给浏览器。 6、在浏览器展示页面内容,实现页面预览的功能。 copntroller 最终在浏...

weixin_30566063的博客 191

阶段5 3.微服务项目【学成在线】_day04 页面静态化_12-页面静态化-页面静态化流程...

需要知道数据结构,然后去做模板标签。首先需要获取页面的数据模型。下面的每一条记录都代表一个页面。 比如这个轮播图。就需要提前给这个轮播图编写一个模板 有很多的页面如果知道每个页面的dataUrl。例如轮播图要生成静态页,谁开发的轮播图这个功能,就跟谁要这个dataUrl gridFS是mongoDB的分布式文件系统。 以上就是分析页面静...

weixin_30566063的博客 222

微服务[学成在线] day10:课程发布、ElasticSearch

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day10 的内容 课程发布功能开发 ElasticsSearch 安装部署 ElasticsSearch 快速入门、IK 分词器、映射、索引。 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、课程发布0x01 需求分析0x02 CMS一键

通往体面生活的路上 1205

阶段5 3.微服务项目【学成在线】_day04 页面静态化_10-freemarker静态化测试-基于模板文件静态化...

把resource拷贝到test目录下 只保留结构和test1.ftl这个模板文件就可以了。 新建一个包 编写测试类 使用freemaker提供的方法生成静态文件 手动的设置模板的路径。获取当前类的classPath然后拼上template的路径 获取test1.ftl这个模板 定义获取数据的方法。...

weixin_30566063的博客 206

微服务[学成在线] day05:消息中间件 RabbitMQ

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day05 的内容 RabbitMQ 的基本应用场景 使用原生RabbitMQ 库构建生产者与消费者模型 整合 Springboot 实现 RabbitMQ 生产者与消费者模型 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、需求分析二、初

通往体面生活的路上 846

java ee实战教程_黑马JavaEE微服务学成在线视频教程学习平台项目实战

│ 学成在线-每日目标-V1.2.xlsx│├─day01 搭建环境 CMS服务端开发│ ├─代码│ │ xcEduService01.zip│ ││ ├─视频│ │ 01-项目概述-功能构架-项目背景.avi│ │ 02-项目概述-功能构架-功能模块和项目原型.avi│ │ 03-项目概述-技术架构.avi│ │ 04-项目概述...

weixin_28964423的博客 717

最新传智学成在线JAVAEE微服务项目实战视频教程

目录 ├─day01 搭建环境 CMS服务端开发 ├─day02 CMS前端开发 ├─day03 CMS页面管理开发 ├─day04 页面静态化 ├─day05 消息中间件RabbitMQ ├─day06 页面发布 课程管理 ├─day07 课程管理实战 ├─day08 课程图片管理 分布式文件系统 ├─day09 课程预览 Eureka Feign ├─day10 课程发布 ElasticSea...

u011576602的博客 992

阶段5 3.微服务项目【学成在线】_day04 页面静态化_20-页面静态化-静态化测试-填写页面DataUrl...

启动前端和后端。轮播图的数据url可以在这里修改。 随便更新一个地址测试 提交后数据再次编辑 发现url没有变化 在pageService里面把更新的字段加上 成功后,再次编辑打开这个页面 这样我们就可以通过页面来编辑这个dataUrl字段 在添加页面也没有这个dataUrl.后续自己可以在添加页面加上 ...

weixin_30566063的博客 230

微服务实战笔记-学成在线-day04

知识点: freemarker GridFS springBoot + vue 实现文件上传下载 freemarker 简单介绍 freemarker是一种类似于jsp的免费的java模板引擎,他可以基于模板和数据生成数据(可以是html,email,world,excel等等) 类似于freemarker的模板引擎还有jsp,Thymeleaf,Velocity等 数据模型 数据模型在java中可以是基本类型,也可以是List,Map,Pojo等复杂类型 模板 通过数据模型和模板一起生成最终的文件 下

sssdal19995的博客 543

阶段5 3.微服务项目【学成在线】_day04 页面静态化_16-页面静态化-模板管理-模板制作...

这是轮播图的原始文件 运行门户需要把 nginx启动起来 单独运行轮播图。把里面的css的引用都加上网址的url 这就是单独访问到的轮播图的效果 把原始文件复制过来。然后改了名字叫做ftl 新加一个方法 返回为轮播图的模板地址 页面没显示东西。但是里面的内容是有的。里面的图片地址都是相对的路径 如果模板这里改成绝对路径...

weixin_30566063的博客 206

阶段5 3.微服务项目【学成在线】_day04 页面静态化_18-页面静态化-模板管理-GridFS研究-取文件...

需要创建mongoDB的配置类 配置类里面主要创建。GridFSBucket这个对象。这个对象的作用就是用来打开一个下载流 在config下创建MongoConfig。这个时候就需要用到spring的注解。@Configuration。加上这个注解。这个类就相当于是一个Bean。 用这个标识的类,spring的容器子在启动的时候。会扫描到这个Bean,然后就会把这个Bean...

weixin_30566063的博客 181
上一篇: 微服务[学成在线] day03:CMS页面管理开发
下一篇: 微服务[学成在线] day05:消息中间件 RabbitMQ
cory_lin
博客等级 码龄7年 22粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值