2021SC@SDUSC
前言
在前面的四篇文章中,我对 module-article-service-provider
模块中的三个核心 ServiceProvider
类做了详细阐述,而在该模块下还有一部分其它的 ServiceProvider
类,但属于边缘代码,这里不再做分析。
回到 module-article
模块,我们已经分析完了 module-article-model
和 module-article-service
模块,剩余 module-article-search
和 module-article-web
模块。
module-article-search
模块简介
module-article-search
模块下只有一个接口类 ArticleSearcher
,用于提供接口方法以全局搜索文章,其代码如下:
public interface ArticleSearcher {
String HIGH_LIGHT_CLASS = "search-highlight";
void addArticle(Article article);
void deleteArticle(Object id);
void updateArticle(Article article);
Page<Article> search(String keyword, int pageNum, int pageSize);
}
接口很简单,提供了一个字符串常量 HIGH_LIGHT_CLASS
以及增删改查的接口方法,但这里暂时看不出该字符串常量的用法,后面应该会遇到。
通过 IntelliJ IDEA 的类追踪可以得知,一共有五个类实现了 ArticleSearcher
接口,分别是阿里云的智能开放搜索 AliyunOpenSearcher
、JBoot 框架自带的数据库搜索 DbSearcher
、著名的 ElasticSearcher
和 LuceneSearcher
以及无搜索 NoneSearcher
。
在这些类中,NoneSercher
类是一个空类,用于不需要搜索但必须要实现该接口的地方;而 DbSearcher
类则只实现了 search()
方法,并且只是调用了 ArticleService
类中的 searchIndb()
方法,没有什么分析价值。
//io.jpress.module.article.searcher.DbSearcher.search()
public Page<Article> search(String keyword, int pageNum, int pageSize) {
return articleService.searchIndb(keyword, pageNum, pageSize);
}
而其余的三个类则调用了第三方库,超出了分析 JPress 项目的范围,当然主要是我也看得不是很懂,这里不做分析。
module-article-web
模块简介
JPress 项目采用的是 MVC 架构,而在前面的文章我分析的是 Model 和 Service,在该模块下的内容即为 View 和 Controller。因为涉及到 View 模块,所以我们可以在该模块下看到 java
和 webapp
两个文件夹,分别用于存放前端和后端代码。
我们首先从 Controller 开始。
Controller
module-article
模块 Controller 的结构如下,共有四个部分。观察后可知, admin
为管理员对所有文章进行操作的部分,api
为对开放接口进行处理的部分,front
为访问文章内容、类型、标签或搜索进行处理的部分,ucenter
是用户在用户中心对与自己相关的文章进行操作的部分。
这部分是 module-article
模块的核心 Controller 部分,可能需要多篇文章进行分析,我们首先从 admin
开始。
admin
admin
部分一共有 5 个类,其中 _ArticleCommentController
和 _ArticleController
类是管理员对全部文章和全部评论进行修改的类,_MarkdownImport
、_WechatArticleImport
和 _WordpressImport
顾名思义可知 JPress 支持通过 Markdown、微信和 WordPress 直接导入成文章。
该部分下每个类前面都添加了一个下划线,虽然 JPress 没有直接说明其含义,但在 Python 中这种写法被赋予「私有类」的含义,可能是为了和后面 front
部分中重名的两个类进行区分,并指明两者的差别而成。
我们首先来看 _ArticleController
。
_ArticleController
基本结构
该类是管理员对全局文章进行操控的类,顺着该类继承链,我们最终可以得知其来自于 JFinal 框架下 com.jfinal.core
包中的核心类 Controller
。
_ArticleController
→ AdminControllerBase
→ ControllerBase
→ JbootController
→ Controller
。
注解 @RequestMapping
用于请求映射配置,即通过域名加上此映射的地址可以访问该 Controller 资源。
@RequestMapping(value = "/admin/article", viewPath = JPressConsts.DEFAULT_ADMIN_VIEW)
public class _ArticleController extends AdminControllerBase {}
静态常量与成员变量
@Inject
private ArticleService articleService;
@Inject
private ArticleCategoryService categoryService;
@Inject
private MenuService menuService;
ArticleService
和 ArticleCategoryService
类已经在前文介绍过,而 MenuService
是与菜单有关的 Service,但这个类并没有被使用,所以并不是很重要。
文章管理
//io.jpress.module.article.controller.admin._ArticleController.list()
@AdminMenu(text = "文章管理", groupId = "article", order = 0)
public void list() {
String status = getPara("status");
String title = getPara("title");
Long categoryId = getParaToLong("categoryId");
Page<Article> page = StringUtils.isBlank(status)
? articleService._paginateWithoutTrash(getPagePara(), getPageSizePara(), title, categoryId)
: articleService._paginateByStatus(getPagePara(), getPageSizePara(), title, categoryId, status);
setAttr("page", page);
Long draftCount = articleService.findCountByStatus(Article.STATUS_DRAFT);
Long trashCount = articleService.findCountByStatus(Article.STATUS_TRASH);
Long normalCount = articleService.findCountByStatus(Article.STATUS_NORMAL);
setAttr("draftCount", draftCount);
setAttr("trashCount", trashCount);
setAttr("normalCount", normalCount);
setAttr("totalCount", draftCount + trashCount + normalCount);
List<ArticleCategory> categories = categoryService.findListByType(ArticleCategory.TYPE_CATEGORY);
SortKit.toLayer(categories);
setAttr("categories", categories);
flagCheck(categories, categoryId);
render("article/article_list.html");
}
//io.jpress.core.menu.annotation.@AdminMenu
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AdminMenu {
String groupId();
String text();
String icon() default "";
String target() default "";
int order() default 100; //越小在越前面
}
//io.jpress.module.article.controller.admin._ArticleController.flagCheck()
private void flagCheck(List<ArticleCategory> categories, Long... checkIds) {
if (checkIds == null || checkIds.length == 0
|| categories == null || categories.size() == 0) {
return;
}
for (ArticleCategory category : categories) {
for (Long id : checkIds) {
if (id != null && id.equals(category.getId())) {
category.put("isCheck", true);
}
}
}
}
注解 @AdminMenu
是 JPress 项目 io.jpress.core.menu.annotation
包下的内容,用于给 Controller 的方法进行标注,申明此方法为一个后台菜单方法。后台菜单被包含在 Group 里,而 Group 是由 Module 来定义的,JPress 系统也内置了几个 Group,groupId
则用于标识这个方法被放在哪个 Group 里。
而 List()
方法中的写法来自 JFinal 框架的 Action,并使用了 get
/ getPara
系列方法,获取或设置了大量与文章相关的参数,而后则调用了私有方法 flagCheck()
以检查文章类型和类型 ID 是否相同,最后返回 article/article_list.html
。
分类与标签
@AdminMenu(text = "分类", groupId = "article", order = 2)
public void category() {
List<ArticleCategory> categories = categoryService.findListByType(ArticleCategory.TYPE_CATEGORY);
SortKit.toLayer(categories);
setAttr("categories", categories);
long id = getParaToLong(0, 0L);
if (id > 0 && categories != null) {
for (ArticleCategory category : categories) {
if (category.getId().equals(id)) {
setAttr("category", category);
setAttr("isDisplayInMenu", menuService.findFirstByRelatives("article_category", id) != null);
}
}
}
initStylesAttr("artlist_");
render("article/category_list.html");
}
分类与标签方法非常类似,这里贴出分类方法作为示例。
首先通过 Service 获取与该类型有关的文章,接着对类型进行包装,然后逐个检查类型是否正确、一致,最后返回相关的 View。
结语
_AricleController
中有非常多的代码,但是绝大多数差不多,基本的形式与上面的分析相差无几,所以不做过多分析。下面的文章将对其它 Controller 进行分析。