转载

【菜鸟读源码】halo✍源码学习(一)

Halo是一款现代化的个人独立博客系统,给习惯写博客的同学一个更好的选择。据说这是一个较容易读懂的Spring-Boot项目,那我就希望通过这个项目学习前辈的经验。

如有帮助,不胜荣幸。如有错误,欢迎指正!

前言

最早看到这个博客的源码的时候是通过B站up主- CodeSheep 的一个视频: Java企业级开源项目推荐,帮助大家从学习走向实践 ,奈何当时自己知识有限,没有仔细的阅读源码。近日Halo也推出了正式版,我也就抱着学习的心态拜读一下。

首先打开工程,看到整个工程有以下两个明显的变化:

  • 配置文件由properties变为yaml

可以明显的看到,在处理层级关系的时候,properties需要使用大量的路径来描述层级(或者属性),比如environments.dev.url和environments.dev.name。其次,对于较为复杂的结构,比如数组(my.servers),写起来更为复杂。而对应的YAML格式文件就简单很多:

  • 构建工具由meavn变为gradle

因为此前有过Android的开发经验,所以这一改变对我的影响并不大:smile:。gradle逐渐替代meavn应该是目前的趋势,但是目前大部分教学和企业采用的还是以meavn为主,所以此前我也未曾尝试过采用gradle构建项目。由此看出Halo还是很Fashion的:+1:。

Application

首先打开 Application.java 文件,看看有什么学习的地方 。

@SpringBootApplication
@EnableJpaAuditing
@EnableScheduling
@EnableAsync
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass = BaseRepositoryImpl.class)
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        // Customize the spring config location
        System.setProperty("spring.config.additional-location", "file:${user.home}/.halo/,file:${user.home}/halo-dev/");

        // Run application
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        System.setProperty("spring.config.additional-location", "file:${user.home}/.halo/,file:${user.home}/halo-dev/");
        return application.sources(Application.class);
    }
}

发现与我之前Spring-Boot开发两点不一样的地方:

其一:继承SpringBootServletInitializer从而实现configure方法

可能找答案的姿势不对,只零星找到下面几点描述:

1、启动类继承SpringBootServletInitializer类,重写 configure(SpringApplicationBuilder builder`)方法

2、因为想要用web容器启动项目

3、使用外部 servlet 容器

结论:为了Undertow容器!

原因:

1、排除内置的tomcat容器-build.gradle

exclude module: 'spring-boot-starter-tomcat'

2、添加Undertow依赖-build.gradle

implementation 'org.springframework.boot:spring-boot-starter-undertow'

3、配置服务器

server:
  port: 8090
  use-forward-headers: true
  undertow:
    io-threads: 2
    worker-threads: 36
    buffer-size: 1024
    directBuffers: true

结合此前在别人基础上修改的网盘项目得到了相同的印证。

其二:System.setProperty("spring.config.additional-location", "...");

开发者给了以下注释: Customize the spring config location (定制spring配置文件的位置)

存在以下三个疑问:

1、System.setProperty有何用?

setProperty (String prop, String value); 1、 设置指定键对值的系统属性,其中prop:系统属性的名称,value:系统属性的值。注:这里的system,系统指的是 JRE (runtime)system,不是指 OS。 2、System.setProperty相当于一个静态变量,存在内存里面,可以在项目的任何一个地方,通过System.getProperty("变量")来获得

2、为何要设定这个变量?

加载外部配置文件。打包jar运行也不方便修改jar内部数据,通过设置环境变量spring.config.location。因为博客中也有很多配置选项,所以猜想需要从外部读取某些会改变的配置,需要继续阅读源码验证猜想。

3、${user.home}从何而来?

如从前所示user.home应该是一个静态变量,尝试打印:

System.out.println(System.getProperty("user.home"));
//打印出:C:/Users/74472 验证猜想成功
变量 含义
java.version Java 运行时环境版本
java.vendor Java 运行时环境供应商
java.vendor.url Java 供应商的 URL
java.home Java 安装目录
java.vm.specification.version Java 虚拟机规范版本
java.vm.specification.vendor Java 虚拟机规范供应商
java.vm.specification.name Java 虚拟机规范名称
java.vm.version Java 虚拟机实现版本
java.vm.vendor Java 虚拟机实现供应商
java.vm.name Java 虚拟机实现名称
java.specification.version Java 运行时环境规范版本
java.specification.vendor Java 运行时环境规范供应商
java.specification.name Java 运行时环境规范名称
java.class.version Java 类格式版本号
java.class.path Java 类路径
java.library.path 加载库时搜索的路径列表
java.io.tmpdir 默认的临时文件路径
java.compiler 要使用的 JIT 编译器的名称
java.ext.dirs 一个或多个扩展目录的路径
os.name 操作系统的名称
os.arch 操作系统的架构
os.version 操作系统的版本
file.separator 文件分隔符(在 UNIX 系统中是“/”)
path.separator 路径分隔符(在 UNIX 系统中是“:”)
line.separator 行分隔符(在 UNIX 系统中是“/n”)
user.name 用户的账户名称
user.home 用户的主目录
user.dir 用户的当前工作目录

测试运行

Spring-Boot的项目运行起来还是比较简单的 。页面也很美观:+1: 【菜鸟读源码】halo✍源码学习(一)

Controller学习

Spring-boot的项目通常都是从Controller读起,发现一个共同的特点,就是代码都是类似这样一个结构:

private final PostService postService;

private final OptionService optionService;

... .... // 其他的Service

private final ThemeService themeService;

public ContentIndexController(PostService postService,
                              OptionService optionService,
                              ThemeService themeService) {
    this.postService = postService;
    this.optionService = optionService;
    ... .... // 其他的Service
    this.themeService = themeService;
}

@GetMapping
public String index(Model model) {
    return this.index(model, 1, Sort.by(DESC, "topPriority").and(Sort.by(DESC, "createTime")));
}

 @GetMapping(value = "page/{page}")
public String index(Model model,
                    @PathVariable(value = "page") Integer page,
                    @SortDefault.SortDefaults({
                        @SortDefault(sort = "topPriority", direction = DESC),
                        @SortDefault(sort = "createTime", direction = DESC)
                    }) Sort sort) {
    ...//省略   
}

学习收获一:使用构造器注入需要用到的Service

学习收获二:使用多态处理请求

真正起作用的是后者,前者只是为用户添加了参数后调用后者。

return this.index(model, 1, Sort.by(DESC, "topPriority").and(Sort.by(DESC, "createTime")));

学习收获三:RESTful的api设计

原文  https://segmentfault.com/a/1190000020295666
正文到此结束
Loading...