文章目录
前言
文章内容为微服务内容,项目是跟着黑马头条进行的。本次内容为黑马头条的第二天,主要内容是文章列表加载、freemarker、minIO、文章详情,本期文章并未涉及到输入参数和选择频道的查询
一、文章列表加载
1.1 需求分析
一、简单说明:
 
- 首先我们进入到页面的时候就会触发一个加载选项,采用的是默认参数进行首页数据加载展示。
 - 我们可以上拉查看更多的文章。
 - 我们可以下划查看更新的文章。
 
二、页面布局展示:
 
1.2 表结构分析
一、文章信息表:

二、 文章配置表:

三、 文章内容表:

四、 表关系:

五、 为什么分表和分表的好处:
六、 表的拆分原则:
1.3 导入文章数据库
一、导入文章数据库:

二、导入对应实体类:

1.4 实现思路

 1,在默认频道展示10条文章信息
2,可以切换频道查看不同种类文章
3,当用户下拉可以加载最新的文章(分页)本页文章列表中发布时间为最大的时间为依据
4,当用户上拉可以加载更多的文章信息(按照发布时间)本页文章列表中发布时间最小的时间为依据
5,如果是当前频道的首页,前端传递默认参数:
-  
maxBehotTime:0(毫秒)
 -  
minBehotTime:20000000000000(毫秒)—>2063年
 
1.5 接口定义
| 加载首页 | 加载更多 | 加载最新 | |
|---|---|---|---|
| 接口路径 | /api/v1/article/load | /api/v1/article/loadmore | /api/v1/article/loadnew | 
| 请求方式 | POST | POST | POST | 
| 参数 | ArticleHomeDto | ArticleHomeDto | ArticleHomeDto | 
| 响应结果 | ResponseResult | ResponseResult | ResponseResult | 
ArticleHomeDto:
package com.heima.model.article.dtos;
import lombok.Data;
import java.util.Date;
@Data
public class ArticleHomeDto {
    // 最大时间
    Date maxBehotTime;
    // 最小时间
    Date minBehotTime;
    // 分页size
    Integer size;
    // 频道ID
    String tag;
}
 
1.6 功能实现
一、启动nacos:配置

二、编写接口:

三、编写mapper:

 mapper:
package com.heima.article.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @Author Administrator
 * @Date 2023/6/22 20:29
 * @description
 * @Version 1.0
 */
@Mapper
public interface ApArticleMapper extends BaseMapper<ApArticle> {
    public List<ApArticle> loadArticleList (@Param("dto")ArticleHomeDto dto,@Param("type") short type);
}
 
四、编写业务层和控制层并添加网关:
 
# 文章微服务
        - id: article
          uri: lb://leadnews-article
          predicates:
            - Path=/article/**
          filters:
            - StripPrefix= 1
 
五、启动nginx,测试:

有一个大坑这里说一下:
我们在前面添加接口测试工具的时候,添加Kneife4j的时候,并没有导入正确的包,但是可以正常使用,在这里会导致一些问题,删除依赖即可。
二、freemarker
2.1 freemarker简介
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
 
常用的java模板引擎还有哪些?
Jsp、Freemarker、Thymeleaf 、Velocity 等。
1.Jsp 为 Servlet 专用,不能单独进行使用。
2.Thymeleaf 为新技术,功能较为强大,但是执行的效率比较低。
3.Velocity从2010年更新完 2.0 版本后,便没有在更新。Spring Boot 官方在 1.4 版本后对此也不在支持,虽然 Velocity 在 2017 年版本得到迭代,但为时已晚。
2.2 环境搭建&&快速入门
一、freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。
二、需要创建Spring Boot+Freemarker工程用于测试模板。
2.2.1 创建测试工程

 一、在这个专门进行测试的微服务中创建一个freemarker-demo 的测试工程专门用于freemarker的功能测试与模板的测试。
测试entity对象:
package com.heima.freemarker.entity;
import lombok.Data;
import java.util.Date;
/**
 * @Author Administrator
 * @Date 2023/6/23 17:12
 * @description
 * @Version 1.0
 */
@Data
public class Student {
    private String name;//姓名
    private int age;//年龄
    private Date birthday;//生日
    private Float money;//钱包
}
 
controller:
package com.heima.freemarker.controller;
import com.heima.freemarker.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
 * @Author Administrator
 * @Date 2023/6/23 17:15
 * @description
 * @Version 1.0
 */
@Controller
public class HelloController {
    @GetMapping("/basic")
    public String test(Model model) {
        //1.纯文本形式的参数
        model.addAttribute("name", "freemarker");
        //2.实体类相关的参数
        Student student = new Student();
        student.setName("小明");
        student.setAge(18);
        model.addAttribute("stu", student);
        return "01-basic";
    }
}
 
配置文件:
server:
  port: 8881 #服务端口
spring:
  application:
    name: freemarker-demo #指定服务名
  freemarker:
    cache: false  #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
    suffix: .ftl               #指定Freemarker模板文件的后缀名
 
进行一个小测试:
 
注意:这里由于springboot2.2.0版本以后,free marker模板引擎默认的模板文件后缀已经由ftl改变为ftlh,如果你的springboot版本在这以上,那么使用ftl将会无法正常映射,除非你在配置文件中指定路径

我们可以去看看源码:找到依赖—》找到springboot自动配置依赖下的spring.factories
 
找到free marker:快捷键:CTRL+f +名称

 看到如下图所示:
 
 信息如下:踩个坑
 
2.3 freemarker基础
2.3.1 基础语法种类
一、注释,即<#-- -->,介于其之间的内容会被freemarker忽略
<#--我是一个freemarker注释-->
 
二、插值(Interpolation):即 ${..}部分,freemarker会用真实的值代替${…}
Hello ${name}
 
三、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑
<# >FTL指令</#> 
 
四、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。
<#--freemarker中的普通文本-->
我是一个普通的文本
 
2.3.2 集合指令
map:
<#-- Map 数据的展示 -->
<b>map数据的展示:</b>
<br/><br/>
<a href="###">方式一:通过map['keyname'].property</a><br/>
输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
<br/>
<a href="###">方式二:通过map.keyname.property</a><br/>
输出stu2的学生信息:<br/>
姓名:${stuMap.stu2.name}<br/>
年龄:${stuMap.stu2.age}<br/>
 
list:
<#list stus as stu> <#-- stus是model传过来时的名称,as是标准写法,stu是list集合中的每个元素,有点像foreach语法-->
        <tr>
            <td>${stu_index+1}</td> <#--stu_index:取集合元素时默认带的下标,从0开始-->
            <td>${stu.name}</td> <#--元素的名字-->
            <td>${stu.age}</td>
            <td>${stu.money}</td>
        </tr>
    </#list>
 
map和list:
<br/>
<a href="###">遍历map中两个学生信息:</a><br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stuMap?keys as key > <#--取出stuMap中所有的keys并as key 取出key然后遍历-->
        <tr>
            <td>${key_index}</td>
            <td>${stuMap[key].name}</td>
            <td>${stuMap[key].age}</td>
            <td>${stuMap[key].money}</td>
        </tr>
    </#list>
</table>
<hr>
 
2.3.3 if指令
一、if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出
- 指令格式
 
<#if ></if>
 
1、数据模型:
使用list指令中测试数据模型,判断名称为小红的数据字体显示为红色。
2、模板:
<table>
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stus as stu >
        <#if stu.name='小红'>
            <tr style="color: red">
                <td>${stu_index}</td>
                <td>${stu.name}</td>
                <td>${stu.age}</td>
                <td>${stu.money}</td>
            </tr>
            <#else >
            <tr>
                <td>${stu_index}</td>
                <td>${stu.name}</td>
                <td>${stu.age}</td>
                <td>${stu.money}</td>
            </tr>
        </#if>
    </#list>
</table>
 
3、输出:
姓名为“小强”则字体颜色显示为红色。

2.3.4 运算符
1、算数运算符
FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:
- 加法: 
+ - 减法: 
- - 乘法: 
* - 除法: 
/ - 求模 (求余): 
% 
模板代码:
<b>算数运算符</b>
<br/><br/>
    100+5 运算:  ${100 + 5 }<br/>
    100 - 5 * 5运算:${100 - 5 * 5}<br/>
    5 / 2运算:${5 / 2}<br/>
    12 % 10运算:${12 % 10}<br/>
<hr>
 
除了 + 运算以外,其他的运算只能和 number 数字类型的计算。
2、比较运算符
=或者==:判断两个值是否相等.!=:判断两个值是否不等.>或者gt:判断左边值是否大于右边值>=或者gte:判断左边值是否大于等于右边值<或者lt:判断左边值是否小于右边值<=或者lte:判断左边值是否小于等于右边值
= 和 == 模板代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
    <b>比较运算符</b>
    <br/>
    <br/>
    <dl>
        <dt> =/== 和 != 比较:</dt>
        <dd>
            <#if "xiaoming" == "xiaoming">
                字符串的比较 "xiaoming" == "xiaoming"
            </#if>
        </dd>
        <dd>
            <#if 10 != 100>
                数值的比较 10 != 100
            </#if>
        </dd>
    </dl>
    <dl>
        <dt>其他比较</dt>
        <dd>
            <#if 10 gt 5 >
                形式一:使用特殊字符比较数值 10 gt 5
            </#if>
        </dd>
        <dd>
            <#-- 日期的比较需要通过?date将属性转为data类型才能进行比较 -->
            <#if (date1?date >= date2?date)>
                形式二:使用括号形式比较时间 date1?date >= date2?date
            </#if>
        </dd>
    </dl>
    <br/>
<hr>
</body>
</html>
 
比较运算符注意
- **
=和!=**可以用于字符串、数值和日期来比较是否相等 - **
=和!=**两边必须是相同类型的值,否则会产生错误 - 字符串 
"x"、"x "、**"X"**比较是不等的.因为FreeMarker是精确比较 - 其它的运行符可以作用于数字和日期,但不能作用于字符串
 - 使用**
gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>**解释成FTL标签的结束字符 - 可以使用括号来避免这种情况,如:
<#if (x>y)> 
3、逻辑运算符
- 逻辑与:&&
 - 逻辑或:||
 - 逻辑非:!
 
逻辑运算符只能作用于布尔值,否则将产生错误 。
模板代码
<b>逻辑运算符</b>
    <br/>
    <br/>
    <#if (10 lt 12 )&&( 10  gt  5 )  >
        (10 lt 12 )&&( 10  gt  5 )  显示为 true
    </#if>
    <br/>
    <br/>
    <#if !false>
        false 取反为true
    </#if>
<hr>
 
2.3.5 空值处理
1、判断某变量是否存在使用 “??”
用法为:variable??,如果该变量存在,返回true,否则返回false
例:为防止stus为空报错可以加上判断如下:
 <#if stus??>
    <#list stus as stu>
    	......
    </#list>
    </#if>
 
2、缺失变量默认值使用 “!”
-  
使用!要以指定一个默认值,当变量为空时显示默认值
例: ${name!‘’}表示如果name为空显示空字符串。
 -  
如果是嵌套对象则建议使用()括起来
例: ${(stu.bestFriend.name)!‘’}表示,如果stu或bestFriend或name为空默认显示空字符串。
 
2.3.6 内建函数
内建函数语法格式: 变量+?+函数名称
1、和到某个集合的大小
${集合名?size}
2、日期格式化
显示年月日: ${today?date}
 显示时分秒:${today?time}
 显示日期+时间:${today?datetime}
 自定义格式化: ${today?string("yyyy年MM月")}
3、内建函数c
model.addAttribute(“point”, 102920122);
point是数字型,使用${point}会显示这个数字的值,每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用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>inner Function</title>
</head>
<body>
    <b>获得集合大小</b><br>
    集合大小:
    <hr>
    <b>获得日期</b><br>
    显示年月日:      <br>
    显示时分秒:<br>
    显示日期+时间:<br>
    自定义格式化:  <br>
    <hr>
    <b>内建函数C</b><br>
    没有C函数显示的数值: <br>
    有C函数显示的数值:
    <hr>
    <b>声明变量assign</b><br>
<hr>
</body>
</html>
 
内建函数模板页面:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>inner Function</title>
</head>
<body>
    <b>获得集合大小</b><br>
    集合大小:${stus?size}
    <hr>
    <b>获得日期</b><br>
    显示年月日: ${today?date}       <br>
    显示时分秒:${today?time}<br>
    显示日期+时间:${today?datetime}<br>
    自定义格式化:  ${today?string("yyyy年MM月")}<br>
    <hr>
    <b>内建函数C</b><br>
    没有C函数显示的数值:${point} <br>
    有C函数显示的数值:${point?c}
    <hr>
    <b>声明变量assign</b><br>
    <#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
    <#assign data=text?eval />
    开户行:${data.bank}  账号:${data.account}
<hr>
</body>
</html>
 
三、对象存储服务MinIO
3.1 MinIO简介
MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档。由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令可以运行起来。
MinIO兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
S3 ( Simple Storage Service简单存储服务)
基本概念:
- bucket – 类比于文件系统的目录
 - Object – 类比文件系统的文件
 - Keys – 类比文件名
 
官网文档:http://docs.minio.org.cn/docs/
3.2 MinIO特点
-  
数据保护
Minio使用Minio Erasure Code(纠删码)来防止硬件故障。即便损坏一半以上的driver,但是仍然可以从中恢复。
 -  
高性能
作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率
 -  
可扩容
不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
 -  
SDK支持
基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持
 -  
有操作页面
面向用户友好的简单操作界面,非常方便的管理Bucket及里面的文件资源
 -  
功能简单
这一设计原则让MinIO不容易出错、更快启动
 -  
丰富的API
支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等。
 -  
文件变化主动通知
存储桶(Bucket)如果发生改变,比如上传对象和删除对象,可以使用存储桶事件通知机制进行监控,并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等。
 
3.3 开箱使用
3.3.1 安装启动
我们提供的镜像中已经有minio的环境
我们可以使用docker进行环境部署和启动
docker run -p 9000:9000 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data
 
我们直接restart:
 
3.3.2 管理控制台
假设我们的服务器地址为http://192.168.200.130:9000,我们在地址栏输入:http://192.168.200.130:9000/即可进入登录界面。
 现在我们查看控制台:


3.4 快速入门
3.4.1 创建工程,导入pom依赖
创建minio-demo,对应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>heima-leadnews-test</artifactId>
        <groupId>com.heima</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>minio-demo</artifactId>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>7.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>
 

 
3.5 封装MinIO为starter
主要是踩坑的,基础薄弱,scope是元素的作用,:
 compile(默认)
 含义:compile 是默认值,如果没有指定 scope 值,该元素的默认值为 compile。被依赖项目需要参与到当前项目的编译,测试,打包,运行等阶段。
provided
 含义:被依赖项目理论上可以参与编译、测试、运行等阶段,相当于compile,但是再打包阶段做了exclude的动作。
 适用场景:例如, 如果我们在开发一个web 应用,在编译时我们需要依赖 servlet-api.jar,但是在运行时我们不需要该 jar 包,因为这个 jar 包已由应用服务器提供,此时我们需要使用 provided 进行范围修饰。
runtime
 含义:表示被依赖项目无需参与项目的编译,但是会参与到项目的测试和运行。与compile相比,被依赖项目无需参与项目的编译。
 适用场景:例如,在编译的时候我们不需要 JDBC API 的 jar 包,而在运行的时候我们才需要 JDBC 驱动包。
test
 含义: 表示被依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。
 适用场景:例如,Junit 测试。
在测试时加入了scope依赖,导致依赖注入一直失败,所以要把scope去掉。
测试:
 
 










