文章目录
- 前言
- 一、文章列表加载
- 1.1 需求分析
- 1.2 表结构分析
- 1.3 导入文章数据库
- 1.4 实现思路
- 1.5 接口定义
- 1.6 功能实现
- 二、freemarker
- 2.1 freemarker简介
- 2.2 环境搭建&&快速入门
- 2.2.1 创建测试工程
- 2.3 freemarker基础
- 2.3.1 基础语法种类
- 2.3.2 集合指令
- 2.3.3 if指令
- 2.3.4 运算符
- 2.3.5 空值处理
- 2.3.6 内建函数
- 三、对象存储服务MinIO
- 3.1 MinIO简介
- 3.2 MinIO特点
- 3.3 开箱使用
- 3.3.1 安装启动
- 3.3.2 管理控制台
- 3.4 快速入门
- 3.4.1 创建工程,导入pom依赖
- 3.5 封装MinIO为starter
🌕博客x主页:己不由心王道长🌕!
🌎文章说明:黑马头条开发🌎
✅系列专栏:微服务项目
🌴本篇内容:对黑马的黑马头条微服务进行开发讲解🌴
☕️每日一语:这个世界本来就不完美,如果我们再不接受不完美的自己,那我们要怎么活。☕️
🚩 交流社区:己不由心王道长(优质编程社区)
前言
文章内容为微服务内容,项目是跟着黑马头条进行的。本次内容为黑马头条的第二天,主要内容是文章列表加载、freemarker、minIO、文章详情,本期文章并未涉及到输入参数和选择频道的查询
一、文章列表加载
1.1 需求分析
一、简单说明:
- 首先我们进入到页面的时候就会触发一个加载选项,采用的是默认参数进行首页数据加载展示。
- 我们可以上拉查看更多的文章。
- 我们可以下划查看更新的文章。
二、页面布局展示:
1.2 表结构分析
一、文章信息表:
二、 文章配置表:
三、 文章内容表:
四、 表关系:
五、 为什么分表和分表的好处:
- 由于不同信息对应的数据大小不一样,操作时消耗的时间也不一样,所以分表。
- 在某一个数据进行操作时,可能因为其数据大小较大,不会在短时间内完成,当对这个数据进行操作时,又会进行锁表,导致其他资源无法访问,降低了效率。
- 分表是为了解决效率问题,好处应该就是提高了效率,不会进行长时间锁表。
六、 表的拆分原则:
- 将一个表的字段分散到多个表中,每个表存储其中一部分字段
- 1、把不常用的字段单独放在一张表
- 2、把text、blob等大字段拆分出来单独放在一张表
- 3、经常组合查询的字段单独放在一张表中
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;// 分页sizeInteger size;// 频道IDString tag;
}
1.6 功能实现
一、启动nacos:配置
二、编写接口:
三、编写mapper:
- 由于涉及到多表查询和复杂自定义查询,mybatisPlus无能为力,所以需要需要自己编写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: articleuri: lb://leadnews-articlepredicates:- 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去掉。
测试: