2021SC@SDUSC

前言

在前面三篇文章中,我对 JPress 项目的基础框架 JFinal 和 JBoot 在使用层面进行了拆解与分析。在接下来的文章中,我将在代码层面对 JPress 项目中的 module-article 进行分析。

JPress 是一个适合非大型企业降低开发成本而使用的、开源的、快速建站产品,module-article 即为 JPress 项目中文章管理的部分,也是核心部分之一。

主 Maven 工程

Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。其中每个 Maven 工程都有一个基本工作单元,称为 POM (Project Object Model),是一个XML文件,包含了项目的基本信息,用于描述项目如何构建、声明项目依赖等。执行任务或目标时,Maven 会在当前目录中查找并读取 POM,获取所需的配置信息,然后执行目标。

module-article 这个 Maven 工程的 POM 指出该工程的子项目,并列出每个子项目元素指向该模块目录的相对路径。

<modules><module>module-article-model</module><module>module-article-web</module><module>module-article-service</module><module>module-article-service-provider</module><module>module-article-search</module><module>module-article-search-db</module><module>module-article-search-lucene</module><module>module-article-search-es</module><module>module-article-search-opensearch</module>
</modules>

可以看出 module-article 下共有 9 个模块,其中主要的模块有 4 个,分别是 modelwebservicesearch,其余 5 个模块分别是这 4 个主要模块的实现部分。

前文中说到 JFinal 是按照 MVC 架构进行项目设计的,而 JPress 项目是以 JFinal 框架作为基础开发的,因此 module-article 跟随了基础框架架构设计的脚步。由此我们得出,能够引导整个项目进程的、最终引出关键代码分析的模块应该是 4 个主要模块中的 service,所以我们将着重对 service 部分进行分析。

在了解 service 之前,我们首先要了解 Article Model 类。

Article Model 类

module-article-service 这个模块中,提供了对文章、文章分类、评论进行操作的接口与抽象类,即 ArticleCategoryServiceArticleCommentServiceArticleService。我将逐步进行拆解分析,首先对文章部分进行分析。

基本结构

对文章部分进行分析,首先需要了解文章的 Model 类 Article 的结构。

@Table(tableName = "article", primaryKey = "id")
public class Article extends BaseArticle<Article> {}

注解 @Table 是在 JBoot 框架中定义的,目的是运行时在源数据库中自动生成一个 Table,并以 id 作为该表的主键。

继承 BaseArticle<M> 抽象类是为了能够使用代码生成器自动生成 JavaBean,并拥有一系列对该 Model 类的操作方法。

静态常量

public static final String STATUS_NORMAL = "normal";
public static final String STATUS_DRAFT = "draft";
public static final String STATUS_TRASH = "trash";

文章的三种状态:正常、草稿、丢弃,使用 staticfinal 进行命名,便于后续的使用和修改。

JavaBean

@JsonIgnore
public boolean isNormal() {return STATUS_NORMAL.equals(getStatus());
}@JsonIgnore
public boolean isDraft() {return STATUS_DRAFT.equals(getStatus());
}@JsonIgnore
public boolean isTrash() {return STATUS_TRASH.equals(getStatus());
}

上述代码有一段细节。在比较两个字符串的时候,使用「常量是否 equals 变量」,即 "trash".equals(getStatus()),而不是「变量是否 equals 常量」,即 getStatus().equals("trash")。其根本原因在于使用后者时,当 getStatus() 查询不到结果,所得结果为 null 时会导致系统出错,从而使系统崩溃,而前者则因为常量不为 null,即使 getStatus() 查询不到结果,返回给调用者的仍然是 false

因为在 JBoot 框架中可以通过在 Controller 中编写带有注解 @JsonBody 的方法实现 Json 的接收,那么就需要一个注解来标注 Model 中哪些字段需要在 Json 转换中忽略,注解 @JsonIgnore 应运而生。通过注解 @JsonIgnore 可以忽略 Json 转换中的某个字段,因上述方法符合 JBoot 框架中 Json 转换方法的格式,所以必须要添加注解 @JsonIgnore。上述代码将会忽略如下输出:

{//示例Json"isNormal": false,"isDraft": true,"isTrash": false}

以上只是 Article Model 类少部分 JavaBean 方法,这一类方法只是返回一个对象的变量,分析意义不大。

URL 获取与处理

@JsonIgnore
public String getUrl() {String link = getLinkTo();if (StrUtil.isNotBlank(link)) {return link;}return UrlUtils.getUrl("/article/", StrUtil.isNotBlank(getSlug()) ? getSlug() : getId());
}public String getUrlWithPageNumber(int pageNumber) {if (pageNumber <= 1) {return getUrl();}return UrlUtils.getUrl("/article/", StrUtil.isNotBlank(getSlug()) ? getSlug() : getId(), "-", pageNumber);
}

上述代码是为了获取文章的 URL,因为文章 URL 在数据库中存储为字符串,所以我们可以通过一系列的字符串工具得到 URL。

这里我们深挖一下这些字符串工具,以下是通过 IntelliJ IDEA 追踪得到代码:

//io.jboot.utils.StrUtil.isNotBlank()
public static boolean isNotBlank(Object str) {return str != null && notBlank(str.toString());
}//io.jboot.utils.StrUtil.notBlank()
public static boolean notBlank(String str) {return !isBlank(str);
}//io.jboot.utils.StrUtil.isBlank()
public static boolean isBlank(String str) {if (str == null) {return true;} else {int i = 0;for(int len = str.length(); i < len; ++i) {if (str.charAt(i) > ' ') {return false;}}return true;}
}

很显然这里的 isNotBlank() 方法意味着字符串不为 null 也不为空串。我们对这个方法溯源后得到的关键代码是 isBlank(),即以 C 语言的样式,通过遍历字符串的每一个字符来判断字符串是否为空串。

//io.jpress.commons.utils.UrlUtils.getUrl()
public static String getUrl(Object... paths) {return buildUrl(paths);
}//io.jpress.commons.utils.UrlUtils.buildUrl()
private static String buildUrl(Object... paths) {boolean isWebFlatUrlEnable = JPressOptions.isFlatUrlEnable();StringBuilder url = new StringBuilder(JFinal.me().getContextPath());for (int i = 0; i < paths.length; i++) {Object path = paths[i];if (path == null){continue;}if (isWebFlatUrlEnable) {url.append(toFlat(i, path.toString()));} else {url.append(path);}}return url.append(JPressOptions.getAppUrlSuffix()).toString();
}//io.jpress.commons.utils.UrlUtils.toFlat()
private static String toFlat(int index, String path) {char[] chars = new char[path.length()];path.getChars(index == 0 ? 1 : 0, chars.length, chars, index == 0 ? 1 : 0);for (int i = 0; i < chars.length; i++) {if (chars[i] == '/') {chars[i] = '-';}}if (index == 0) {chars[0] = '/';}return new String(chars);
}

buildUrl() 方法是首先判断配置文件是否有开启 isWebFlatUrlEnable,该配置在 JFinal 框架下,意为 URL 是否为平坦的,如果开启,则进行 toFlat()。这里其实不是很好翻译,而 toFlat() 方法是将 /xxx/xxx 类型的 URL 转换为 /xxx-xxx,所以开发者将 /xxx-xxx 类型的 URL 视为平台的 URL,而 /xxx/xxx 视为不平坦的。

最后 buildUrl() 方法将创建好的 URL 返回给 Service 层。

与数据库交互

//io.jboot.db.model.JbootModel.save()
@Override
public boolean save() {CommonsUtils.escapeModel(this, "content", "summary");JsoupUtils.clean(this,"content","summary");return super.save();
}//io.jboot.db.model.JbootModel.updates()
@Override
public boolean update() {CommonsUtils.escapeModel(this, "content", "summary");JsoupUtils.clean(this,"content","summary");return super.update();
}//io.jpress.commons.utils.CommonsUtils.escapeModel()
public static void escapeModel(Model model, String... ignoreAttrs) {String[] attrNames = model._getAttrNames();for (String attr : attrNames) {if (ArrayUtils.contains(ignoreAttrs, attr)) {continue;}Object value = model.get(attr);if (value != null && value instanceof String) {model.set(attr, StrUtil.escapeHtml(value.toString()));}}
}//io.jpress.commons.utils.JsoupUtils.clean()
public static void clean(Model model, String... attrs) {if (attrs != null && attrs.length == 0) {return;}for (String attr : attrs) {Object data = model.get(attr);if (data == null || !(data instanceof String)) {continue;}model.set(attr, clean((String) data));}
}

这是实现 JBoot 框架 Model 存入数据库方法,其中 escapeModel()clean() 方法是 JPress 项目声明的,这两个方法将 contentsummary 字段清除,为的是防止 Model 存储关于 XSS 跨站脚本攻击相关代码。

结语

本文分析了 JPress 项目 module-article 模块下的 Model 类 Article,对该实体类的方法做了详细的分析。在接下来的文章中,我们将对该模块的 module-article-model 做详细分析。

<2021SC@SDUSC>博客(5)山东大学软件工程应用与实践JPress代码分析(四)相关推荐

  1. <2021SC@SDUSC>博客(9)山东大学软件工程应用与实践Jpress代码分析(8)

    2021SC@SDUSC Lucene和ElasticSearch 在上一篇博客中提到的Elasticsearch是一个基于Lucene搜索引擎为核心构建的开源,分布式,RESTful搜索服务器.这里 ...

  2. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(三)

    2021SC@SDUSC 目录 一.概述 二.代码分析 1.Update() 2.Draw() 3.Layout() 一.概述 本文将介绍ebiten在RunGame函数中逐帧执行的Update()方 ...

  3. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(四)

    2021SC@SDUSC 目录 一.概述 二.代码分析 1.graphics.QuadVertices() 2.mipmap.Mipmap() 3.i.mipmap.DrawTriangles() 一 ...

  4. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(十三)metrics.py-1

    2021SC@SDUSC 前言 这篇分析metrics.py文件,这个文件是用来计算评估指标,包括mAP.混淆矩阵.IOU相关的函数. fitness函数 def fitness(x):# Model ...

  5. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(四)general.py-2

    2021SC@SDUSC 目录 前言 is_writeable函数 is_docker函数 is_colab函数 is_pip函数 is_ascii函数 file_size函数 check_onlin ...

  6. 2021SC@SDUSC山东大学软件学院软件工程应用与实践--YOLOV5代码分析(八)plots.py-1

    2021SC@SDUSC 前言 这篇分析plot.py文件,就如其名称一样,主要是一些用以展示的代码,也不是核心代码 外部库 from copy import copy from pathlib im ...

  7. 山东大学软件工程应用与实践——Pig代码综述

    2021SC@SDUSC 目录 一.Pig是什么? 二.Pig的特点 三.Pig安装部署 四.组内分工 一.Pig是什么? Pig 是Apache平台下的一个免费开源项目,是MapReduce的一个抽 ...

  8. 山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析

    2021SC@SDUSC 目录 一.引言 二.密钥的生成数字签名与签名验证相关代码 1.判定函数 2.签名的初始化函数 3.签名执行函数 4.真正的签名函数 5.签名验证初始化函数 6.签名验证执行函 ...

  9. <2021SC@SDUSC>博客(1)山东大学软件工程应用与实践-038号JPress小组课题介绍

    2021SC@SDUSC Jpress项目介绍 JPress,一个使用Java开发,类似WordPress的产品.天生融合微信生态系统,简单易上手.致力于为企业打造自主自属的营销平台,用技术助力企业营 ...

最新文章

  1. 仿真环境跟车2分钟,就让自动驾驶系统撞上马路牙子,攻破率超90%,多传感器融合系统都失效...
  2. ML:MLOps系列讲解之系列知识解读全貌
  3. arm-none-eabi-gcc install
  4. python二十四点_Python秒算24点,行还是不行?
  5. word2010赠送_我们将赠送两台LulzBot 3D打印机
  6. Windows Print Spooler 被曝未修复 0day,可导致恶意软件以管理员权限运行
  7. crontab 不执行解决方案
  8. java将中国标准模式转换成yyyy-mm-nn_java-IO
  9. 《How to Use the TimeDistributed Layer for Long Short-Term Memory Networks in Python》学习笔记
  10. X86平台下基于grub2+busybo+linux-2.6.36制作linux系统
  11. 【BZOJ1923】[Sdoi2010]外星千足虫 高斯消元
  12. [公告]新增项目交流区
  13. Java简易电影院系统
  14. 中国有多少家银行 最全名单统计
  15. springMVC 生成Excel和PDF
  16. 记一次修复Mac和Win7双系统启动菜单的经历
  17. 护眼的绿豆沙色 RGB 值
  18. OverFeat——全卷积首次用于检测问题 (目标检测)(深度学习)(ICLR 2014)
  19. 用函数发生器输出高阻态程序
  20. 学习一个 Linux 命令:realpath 命令

热门文章

  1. 计算机系统之定量分析
  2. POJ 2395 Out of Hay 最小生成树 Kruskal
  3. 性能测试模型初探及应用方法分析
  4. SAP中采购收货控制中的配置问题分析
  5. 计算机网络自顶向下方法 第二章套接字编程作业 邮件客户 答案
  6. [最新]Myeclipse 10.7.1 激活工具及过程详解 亲测
  7. 7-12 哥尼斯堡的“七桥问题” (25 分)(并查集)
  8. 11矩阵空间、秩1矩阵和小世界图
  9. eNSP实验日记二划分Vlan
  10. 浅谈大数据领域的云计算