iTranswarp 是廖雪峰大神官方网站的开源 CMS,用来托管个人的网站,简洁够用。
1 技术架构
iTranswarp 主体上是使用了 Spring Boot 2.2.6 的一个单体应用,其页面模板引擎为 pebbletemplates,并且使用了 redis 缓存和全文检索 lucene,数据存储使用其自定义的简化数据持久框架 warpdb,其底层注入 JdbcTemplate 完成数据持久化。
数据库是常用的 MySQL,使用了 HikariCP 数据源连接池。
Markdown 解析器使用的是 commonmark-java:一个基于 CommonMark 规范解析和渲染 Markdown 文本的 Java 库,特点是小、快、灵活。后续需要通过这一块扩展 gitbook 内容直接导入(wiki)的功能。
使用了 JDK 11。
2 程序分析
2.1 数据持久化
iTranswarp 的数据持久化是通过其自定义的简化数据持久框架 warpdb 来完成的。
在 WarpDb 类里面使用了 Spring 的 JdbcTemplate 来完成最终的数据持久化操作。
public class WarpDb {
final Log log = LogFactory.getLog(getClass());
JdbcTemplate jdbcTemplate;
warpdb 持久化框架最重要的一个类是范型化的 Mapper
类:
final class Mapper {
final Class entityClass;
final String tableName;
// @Id property:
final AccessibleProperty[] ids;
// @Version property:
final AccessibleProperty version;
// all properties including @Id, key is property name (NOT column name)
final List allProperties;
// lower-case property name -> AccessibleProperty
final Map allPropertiesMap;
final List insertableProperties;
final List updatableProperties;
// lower-case property name -> AccessibleProperty
final Map updatablePropertiesMap;
final BeanRowMapper rowMapper;
final String selectSQL;
final String insertSQL;
final String insertIgnoreSQL;
final String updateSQL;
final String deleteSQL;
final String whereIdsEquals;
final Listener prePersist;
final Listener preUpdate;
final Listener preRemove;
final Listener postLoad;
final Listener postPersist;
final Listener postUpdate;
final Listener postRemove;
...
可以通过 ArticleService 文章服务类的 createArticle 方法看到清晰的数据操作过程。
@Transactional
public Article createArticle(User user, ArticleBean bean) {
bean.validate(true);
getCategoryById(bean.categoryId);
Article article = new Article();
article.id = IdUtil.nextId();
article.userId = user.id;
article.categoryId = bean.categoryId;
article.name = bean.name;
article.description = bean.description;
article.publishAt = bean.publishAt;
article.tags = bean.tags;
AttachmentBean atta = new AttachmentBean();
atta.name = article.name;
atta.data = bean.image;
article.imageId = attachmentService.createAttachment(user, atta).id;
article.textId = textService.createText(bean.content).id;
this.db.insert(article);
return article;
}
- 创建实体 Article(使用 JPA 注解 @Entity、@Table、@Column 等进行标注),并设置各种属性;
- 调用 WarpDb 的 insert 方法,将实体 Article 存入数据库;
持久化框架通过传入的实体对象获取其类其 Mapper
,构建对应的 sql,最终通过 jdbcTemplate 执行这段sql,将其存储到数据库中。
private boolean doInsert(boolean ignore, T bean) {
try {
int rows;
final Mapper> mapper = getMapper(bean.getClass());
final String sql = ignore ? mapper.insertIgnoreSQL : mapper.insertSQL;
...
rows = jdbcTemplate.update(sql, args);
...
}
2.2 视图模板
iTranswarp 的视图模板使用的是不太常见、但是效率较高的 Pebble Templates:简单高效,容易上手。
Pebble 官方提供了 Spring Boot 的 starter 集成,但是 iTranswarp 使用了原始的集成方式:在 MvcConfiguration 类中注册了 ViewResolver 为 Spring MVC 提供视图解析器。
@Bean
public ViewResolver pebbleViewResolver(@Autowired Extension extension) {
// disable cache for native profile:
boolean cache = !"native".equals(activeProfile);
logger.info("set cache as {} for active profile is {}.", cache, activeProfile);
PebbleEngine engine = new PebbleEngine.Builder().autoEscaping(true).cacheActive(cache).extension(extension)
.loader(new ClasspathLoader()).build();
PebbleViewResolver viewResolver = new PebbleViewResolver();
viewResolver.setPrefix("templates/");
viewResolver.setSuffix("");
viewResolver.setPebbleEngine(engine);
return viewResolver;
}
- 视图解析前缀为 templates/;
- 视图解析后缀为空;
以 ManageController 控制器为例,看看其中的“新建文章” 服务的代码:
@GetMapping("/article/article_create")
public ModelAndView articleCreate() {
return prepareModelAndView("manage/article/article_form.html", Map.of("id", 0, "action", "/api/articles"));
}
- 使用 "templates/manage/article/article_form.html" 这个模板。
模板文件 _base.html 是最基础的页面,可以在其上添加你需要的内容,例如网站备案信息。
为了简便起见(毕竟我只用一次),硬编码添加,没有扩展为“管理控制台”里面的设置属性。