spring 系框架如 spring, springboot, springcloud 等已成为 java 开发的标配,笔者也是 spring 系框架的多年用户,它们遵循统一的原则,原理清晰,实现优雅, 更新及时,很多最佳实践慢慢都沉淀为规则,为广大 javaer 提供了很多开箱即用的便利,大大降低开发成本。

官方网站一般是学习和查阅某种技术资料的首选, spring 也不例外. 前段时间,我惊喜的发现它们的官方网站 改版了, 风格更加清爽轻量,访问速度更快,布局更加条理找文档更加方便了。在这个前端框架层出不穷的年代,我有点好奇 spring 的官网前端和后端分别是用 啥做的, 又有啥亮点呢? 我们来一探究竟。

首页截图

分析网站技术架构有一个不错的工具 wappalyzer, 不过我们这里用不到,因为他家官网源码是开源的。 把源码 clone 下来,README 上面一开始就写着:

In addition to the practical purpose of powering Spring’s home on the web, this project is designed to serve as a reference application– resource that developers can use to see how the Spring team have used Spring to implement a real-world app with a few interesting requirements. We hope you’ll find it useful!

有趣,这个项目是官方推荐的学习项目,开发者可以把这个项目作为一个参考项目,从这个项目学到 spring 团队怎么用 spring 来实现一个真实项目的。 生产环境即spring官网,需求明确,不算复杂也不会太简单,官方实现肯定代码质量也有保证,用来学习非常好。

--sagan
  |-- sagan-client
  |-- sagan-renderer
  |-- sagan-site

这是一个gradle 多模块项目, 项目按功能划分到 3 个子模块。这是一个很好的实践,将项目代码按功能分成多个子模块。

sagan-client

这是前端模块,该模块使用 node.js and npmWebpack 进行编译, 包含所有 web 端的资源:

  • JavaScript 模块和依赖
  • CSS 样式
  • 图片和字体

这个模块有2个有趣的点,我可以学习这种前后端分离的方式:

  • 一是它用 gradle 调用 npm 编译前端,也就是说前端也集成到 gradle 里面
  • 二是它并没有用到 react, vue 等前端框架。这是一个传统的 jquery 项目,但看代码仍然很清晰,而且速度很快(可直接在官网体验)

sagan-renderer

这是渲染模块,简约而不简单,作用是将 markdown asciidoc 等文档渲染成 html 。 这里我们看到了 springboot 一些经典的用法, 如

  • 配置自动注入 , yaml 中的配置可以自动注入到需要的类中,如 RendererProperties

  • spring-boot-starter-hateoas HATEOAS 即 hypertext as the engine of application state, 超媒体即应用状态引擎,它是 Richardson 提出的 REST 成熟的模型中的第 4 层也就是最高一层。 它要求资源不但有链接信息, 还应该有可以执行的动作信息,这样客户端就可以动态发现所有它能执行的动作。 spring-boot-starter-hateoas 就是 HATEOAS 的一个实现, 该仓库有几处用了它,如 GuideResource

  • 远程调用 非微服务架构,也可以方便的使用远程调用,见 GithubClient. 在这里可以学到另一个很有用的方法-使用 springboot 短短几行代码下载 git 仓库:

	/**
	 * Download a repository as a zipball
	 * @param organization the github organization name
	 * @param repository the repository name
	 * @return the zipball as raw bytes
	 */
	public byte[] downloadRepositoryAsZipball(String organization, String repository) {
		try {
			byte[] response = this.restTemplate.getForObject(REPO_ZIPBALL_PATH,
					byte[].class, organization, repository);
			return response;
		}
		catch (HttpClientErrorException ex) {
			throw new GithubResourceNotFoundException(organization, ex);
		}
	}

sagan-renderer

这是核心模块,它获获取内容,渲染成 html,展示给前端。
我们首先来看看它是怎么和 sagan-client 关联起来的。其实比较简单,将静态资源路径添加到 springboot 配置文件即可,如下:

spring:
  profiles: standalone
  resources:
    static_locations:
      - file:${client.dir}/build/dist/
  devtools:
    restart:
      additional_paths:
        - ../sagan-client/build/dist/
      additional_exclude: "**/*.js,**/*.css"

然后它是怎么使用 sagan-client 服务的呢, 见 SaganRendererClient。此外可见项目并不是国内流行的 mybatis 而是 jpa, 经过 springboot-data-jpa 的加持,有很强的灵活性和可读性。再我的下一个项目,我可能也会试试 jpa。

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

    Page<Post> findByCategoryAndDraftFalse(PostCategory category, Pageable pageable);

    Page<Post> findByDraftTrue(Pageable pageRequest);

    @Query("select p from Post p where YEAR(p.publishAt) = ?1 and MONTH(p.publishAt) = ?2 and DAY(p.publishAt) = ?3")
    Page<Post> findByDate(int year, int month, int day, Pageable pageRequest);
}

最后, sagan-renderer 是一个常规的 mvc 项目,视图层使用 thymeleaf, 将结果返回给浏览器。这样,整个流程就完成了。

最后,我们来总结一下,这个项目可以学到什么.

  • gradle 多模块项目,包括和 npm 等前端的整合
  • springboot mvc 开发, 包括多环境支持, jpa,cache, 远程调用,thymeleaf 等
  • 其他最佳实践,如代码规范包括命名和注释,脚本, flyway管理数据库变化,第三方服务如github,cloudfoundry

感谢您的阅读。