【Spring Boot】搭建个人博客 - 后端环境搭建

我不能停滞不前

前言

诸君,搭建个人博客系列,我终于开始更了。。没想到还有人催更,受宠若惊!
在阅读本文前,我假设您除了Spring Boot,还对以下技术有了解

  • ORM框架:Mybatis-Plus
  • 插件工具:Lomok
  • 数据库连接池:Druid
  • 模板引擎:Freemark,Thymeleaf
  • API文档框架:Swagger

为了用户体验,正文部分是一气呵成的,搭建过程中所遇问题,可以看踩坑纪部分。
提醒一下:
若是您要参考正文部分来搭建环境,请务必看踩坑纪部分,否则只看正文部分,不便于理解且可能项目会跑不起来。


好吧。。其实这里本来还有一些话的,但是发现写的太长了。。也是为了不影响读者体验,放在了后记中;毕竟各位是来看技术文章的,不是看我瞎BB的,不能主次颠倒。
开始正文

正文

1. 数据库设计

数据库设计是最初的阶段,同时我也是卡在这阶段最久。至于为什么卡这么久请看踩坑纪部分
根据我上一篇的【Spring Boot】搭建个人博客 - 需求分析设计数据库表。
数据库设计规范主要参考阿里巴巴Java开发手册 1.4.0,以及一些网上资料;
表名前缀是模块名,分了8个模块。
一共18张表,其中16张实体表,2张多对多关系表;
sql文件会与项目一同上传至Github

【Spring Boot】搭建个人博客 - 后端环境搭建_第1张图片
数据库表结构

2. idea创建Spring boot项目

具体创建过程参考这里:【Spring Boot】初体验
给出application.properties和pom.xml


application.properties

#上下文
server.servlet.context-path=/blog

#日志配置
logging.level.org.springframework.web=DEBUG

#Thymeleaf 配置
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
##缓存设置为false, 这样修改之后马上生效,便于调试;生产环境下可以设置为true
spring.thymeleaf.cache=false

#Druid配置
##JDBC配置
spring.datasource.druid.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&useSSL=false&characterEncoding=utf8
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=root
##连接池配置
spring.datasource.druid.max-active=20


#Freemarker配置
##不使用文件系统优先,而使用classpath下的资源文件优先,解决打jar包运行后,出现的异常问题
spring.freemarker.prefer-file-system-access=false

pom.xml



    4.0.0

    top.sinch
    blog
    0.0.1-SNAPSHOT
    jar

    blog
    Blog project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.4.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            org.springframework.boot
            spring-boot-devtools
            true
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.0.5
        

        
        
            mysql
            mysql-connector-java
            runtime
        

        
        
            com.alibaba
            druid-spring-boot-starter
            1.1.10
        

        
        
            io.springfox
            springfox-swagger2
            2.9.2
        
        
            io.springfox
            springfox-swagger-ui
            2.9.2
        

        
        
            org.projectlombok
            lombok
            1.18.4
            provided
        

        
        
            com.google.code.gson
            gson
            2.8.5
        

        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        

        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        Thymeleaf的非严格HTML格式包-->
        
            net.sourceforge.nekohtml
            nekohtml
            1.9.22
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    




3. Mybatis-Plus代码生成器生成模板代码

使用Mybatis-Plus官方提供的代码生成器Demo生成模板代码。
需要注意的是:

  • 生成器根据数据库表名,一次生成的是同一模块下代码;若有几个模块则需要更改模块名及相对应表名;有几个模块,执行几次生成器生成对应模块下的代码
  • 若只有一个模块或者不区分模块,则只用执行一次生成器

新建包路径
src\main\java下新建包路径com.baomidou.mybatisplus,把代码生成器和项目代码区分开

【Spring Boot】搭建个人博客 - 后端环境搭建_第2张图片
项目结构

代码生成器(把官方Demo根据需求改造了一下)

package com.baomidou.mybatisplus;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;

/**
 * mybatis-plus 代码生成器
 *
 * @Author sincH
 * @Date 2018/11/10
 */
public class CodeGenerator {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        //项目根目录
        String projectPath = "D:/_Code/idea/blog";
//        String projectPath = "C:/Users/sincH/Desktop/Temp/blog";
        //Java源码输出目录
        gc.setOutputDir(projectPath + "/src/main/java");
        //作者
        gc.setAuthor("sincH");
        //是否打开输出目录,默认true
        gc.setOpen(false);
        //开启swagger2模式(将实体类默认的Javadoc文档注解转为Swagger文档注解),默认false
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        //数据库连接URL
        dsc.setUrl("jdbc:mysql://localhost:3306/blog?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");//数据库模式;MySQL中库即模式
        //数据库驱动
        dsc.setDriverName("com.mysql.jdbc.Driver");
        //数据库用户名
        dsc.setUsername("root");
        //数据库密码
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //模块名
        pc.setModuleName("article");
        //父包名;模块将在父包下生成
        pc.setParent("top.sinch.blog");
        mpg.setPackageInfo(pc);

        // 自定义配置
//        InjectionConfig cfg = new InjectionConfig() {
//            @Override
//            public void initMap() {
//                // to do nothing
//            }
//        };
//        List focList = new ArrayList<>();
//        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
//            @Override
//            public String outputFile(TableInfo tableInfo) {
//                // 自定义输入文件名称
//                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
//                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
//            }
//        });
//        cfg.setFileOutConfigList(focList);
//        mpg.setCfg(cfg);
        //setXml(null)代表不生成xml文件
        mpg.setTemplate(new TemplateConfig().setXml(null));

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //表名下划线转驼峰
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //列名下划线转驼峰
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //Boolean类型字段是否移除is前缀(默认 false)
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);
//        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
        //是否开启实体类Lombok模式(默认false)
        strategy.setEntityLombokModel(true);
        //是否开启RestController风格
        strategy.setRestControllerStyle(true);
//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        //表名(数据库中存在的表);多表传数组
        strategy.setInclude(new String[]{
                "article_info","article_content","article_comment","article_comment_reply",
                "article_archive","article_label","article_category",
                "r_article_info_article_label","r_article_info_article_category"
        });
//        strategy.setSuperEntityColumns("id");
        //驼峰转连字符(是否开启Controller映射连字符风格)
        strategy.setControllerMappingHyphenStyle(true);

        /**
         *设置表名前缀;
         *此表名前缀若与数据库表名前缀一致,则自动创建实体类时,实体类名匹配数据库表名下划线后面的名;
         *栗子:数据库表名为 admin_info ,当strategy.setTablePrefix("admin_")时,则实体类名为Info;
         *若不设置表名前缀,则实体类名匹配数据库表名
         */
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);

        //设置代码生成器的模板引擎
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        //执行代码生成器
        mpg.execute();
    }
}


需要注意的是:
把代码生成器生成的代码输出目录设置为项目地址,这样就可以不用在代码生成后还要手动复制到项目中。

【Spring Boot】搭建个人博客 - 后端环境搭建_第3张图片
输出目录与项目地址对应


生成器生成结果及其对应模块
这里是9个模块,多了一个core(核心)模块;是放项目的配置类,拦截器,过滤器等等,其实也可以不用建;不建的话,配置类那些比较散乱分布,我觉得还是集中比较好,就建了个core包

【Spring Boot】搭建个人博客 - 后端环境搭建_第4张图片
需求对应模块



修改生成器生成的模板代码命名
有时候,模板代码的命名不太符合需求,比如我这里statistics(统计)模块下有个统计IP的子模块,模板代码用了驼峰命名把IP命名为Ip,我觉得还是都用大写字母表示IP好

【Spring Boot】搭建个人博客 - 后端环境搭建_第5张图片
statistics模块

4. 单元测试

基于以上,若是单模块项目或者多模块项目无同名Class的话,项目的后端环境搭建就基本到此完成了,写个单元测试;不会写单元测试的请参考嘟嘟独立博客的这篇文章:Spring Boot干货系列:(十二)Spring Boot使用单元测试。

新建包路径
test文件下新建要进行单元测试的类的相对应包路径;
这里举例,要进行单元测试的类是ContentMapper

【Spring Boot】搭建个人博客 - 后端环境搭建_第6张图片
ContentMapper类单元测试

ContentMapper单元测试

package top.sinch.blog.article.mapper;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import top.sinch.blog.BlogApplication;
import top.sinch.blog.article.entity.Content;

import javax.annotation.Resource;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ContentMapperTest {
//    @Autowired(required = false)//允许依赖对象为null
    @Resource
    private ContentMapper contentMapper;

    @Test
    public void insert() {
        Content content = new Content();
        content.setDetail("23333333333333333333333333333333");
//        content.setArticleInfoId(Integer.toUnsignedLong(1));
        int result = contentMapper.insert(content);
        Assert.assertThat(result, equalTo(1 ));
    }

}

单元测试结果
test passed:测试通过

单元测试结果

但是请注意,我这里强调的是单模块项目或者多模块项目无同名Class,而我的这个个人博客项目是多模块且不同模块下有同名Class的,所以您会发现单元测试会失败,报以下错误或者类似错误

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [top.sinch.blog.BlogApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'infoController' for bean class [top.sinch.blog.admin.controller.InfoController] conflicts with existing, non-compatible bean definition of same name and class [top.sinch.blog.about.controller.InfoController]

这个坑我就不放在踩坑纪部分说了,在这里解释;
出现这个错误的原因:
是因为本项目中不同模块下都有一个Info实体类,相对应的就都有InfoController,InfoServiceImpl,InfoMapper;若不为不同模块下的InfoController,InfoServiceImpl,InfoMapper取别名的话,这些InfoController等在注册进Spring容器时就会失败;因为Spring默认是用类名进行Bean注册的,而它们类名又都是InfoController等,自然会失败


这里举例about模块和admin模块,以便于理解

【Spring Boot】搭建个人博客 - 后端环境搭建_第7张图片
about模块和admin模块

【Spring Boot】搭建个人博客 - 后端环境搭建_第8张图片
about模块和admin模块的Controller

解决方法
为不同模块下的InfoController,InfoServiceImpl,InfoMapper取别名;
同样举例,about模块和admin模块。

【Spring Boot】搭建个人博客 - 后端环境搭建_第9张图片
为不同模块下InfoController取别名

然后再进行单元测试,完美通过!Perfect!!

5. 设计RESTful API

初次尝试在项目中设计RESTful API;这里鉴于篇幅原因,仅给出一个Demo,其余都是类似的;不过在Demo之前先讲讲RESTful API设计规范,规范主要参考阮一峰的这篇RESTful API 最佳实践


RESTful API 设计规范
阮一峰的RESTful API 最佳实践推荐使用复数URL,但是我根据项目情况并没有采用复数URL;
这里以article模块下的InfoController为例子

  • GET /article/info/{id} 获取单个文章信息
  • GET /article/info/all 获取所有文章信息
  • POST /article/info 新增一篇文章信息
  • PUT /article/info 更新一篇文章信息
  • DELETE /article/info/{id} 删除一篇文章信息

swagger2配置类

package top.sinch.blog.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger2配置类
 *
 * @Author sincH
 * @Date 2018/11/14
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("top.sinch.blog"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("sincH个人博客RESTful APIs")
                .description("sincH个人博客API文档")
                .contact(new Contact("sincH","https://www.jianshu.com/u/64e4e9db42c9","[email protected]"))
                .version("1.0")
                .build();
    }
}


article模块下的InfoController

package top.sinch.blog.article.controller;


import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import top.sinch.blog.article.entity.Info;

/**
 * 

* 前端控制器 文章信息 *

* * @author sincH * @since 2018-11-20 */ @RestController("articleInfoController") @RequestMapping("/article/info") public class InfoController { @ApiOperation("获取单个文章信息") @GetMapping("/{id}") public String get(@PathVariable("id") String id) { Info articleInfo = new Info(); articleInfo.setAuthor("sincH"); articleInfo.setTitle("Test"); return new Gson().toJson(articleInfo); } @ApiOperation("获取所有文章信息") @GetMapping("/all") public String list(){ return "list successful"; } @ApiOperation("新增一篇文章信息") @PostMapping("") public String save(){ return "save successful"; } @ApiOperation("更新一篇文章信息") @PutMapping("") public String update(){ return "update successful"; } @ApiOperation("删除一篇文章信息") @DeleteMapping("/{id}") public String remove(@PathVariable("id") String id){ return "remove successful"; } }

除了以上步骤外,肯定要先导入swagger相关依赖包;
有认真看的看官,应该知道我已经先在pom.xml中导入了swagger相关依赖包
所以设计完RESTful API,直接在浏览器输入地址:
http://localhost:8080/blog/swagger-ui.html
若您更改了端口和项目名,请相对应更改地址;不然肯定访问不了(感觉说这话有点多余)

【Spring Boot】搭建个人博客 - 后端环境搭建_第10张图片
swagger

对某个RESTful API进行测试

【Spring Boot】搭建个人博客 - 后端环境搭建_第11张图片
RESTful API测试

踩坑纪

纪一 :命名规范问题

命名规范是我这么久都没更新文章的主要原因;我因为命名问题卡在了数据库设计阶段超级久;每天上班前的一小时,下班后的两小时都在想数据库表名,字段名,类名等怎么命名会对后面的开发比较友好;
没错!您也不用怀疑;就是命名问题卡了我许久,也不是什么技术大问题;因为我对命名有些许强迫症,命名不好就会推翻重来;不过倘若是别人一开始就设计好的命名,我其实也不会太纠结。关键这是我自己设计的就会十分纠结。
光是最初的数据库表名设计,我就有好几种命名;


以文章信息表为例,有以下命名

  • article
  • article_base
  • article_article_info
  • article_info

命名规范参考了阿里巴巴Java开发手册 1.4.0,以及一些网上资料;
接下来逐个分析为什么我使用这个命名,以及为什么抛弃这个命名;可能比较长,希望您有耐心看;可能有些人觉得没必要这么纠结命名,那么您可以跳过这一纪,看下一个坑的纪录。

  • article

    • 为什么使用这个命名
      article这命名其实不用过多解释,当你想设计文章表时,那么article肯定是你的首选命名,可能您看到过essay这个命名,当然也是可以的,但是推荐使用article
    • 为什么抛弃这个命名
      根据阿里巴巴Java开发手册 1.4.0中的MySQL数据库(一)建表规约第8条
    1. 【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

    那么article(文章)表中肯定有个content(内容)字段,而content字段长度不确定,有可能会超过5000,所以我决定将content独立成表,命名为article_content;
    那么article,article_content不太统一,要么都是“XXX_XXX”格式,要么就都不是比较好;而且根据阿里巴巴Java开发手册 1.4.0中的MySQL数据库(一)建表规约第10条

    1. 【推荐】表的命名最好是加上“业务名称_表的作用”。 正例:alipay_task / force_project / trade_config

    因此将article重命名为article_base

  • article_base

    • 为什么使用这个命名
      因为抛弃了article命名,所以就改成了article_base;可能有人会说为什么不用article_info呢,因为我觉得文章表直接命名article就行了,如果命名article_info有点多此一举,但是又要分表,所以就想到了article_base,直译为文章基础表,以后要是想扩展表,可以命名为article_extra之类的,妙啊
    • 为什么抛弃这个命名
      因为使用MyBatis-Plus的代码生成器时,倘若设置了表名前缀且表名前缀与数据库表名前缀一致,那么代码生成器生成的实体类名为表名前缀后的名。
      举个栗子:
      也就是说倘若代码生成器里设置了表名前缀为"article_",而数据库表名为"article_base",则实际由代码生成器生成的实体类名为Base。
      那么实体类名为Base,不如Info来得更直观友好,因此抛弃article_base命名
       //省略前面的配置

        //模块名
        pc.setModuleName("article");

       //省略中间的配置

        //表名(数据库中存在的表);多表传数组
        strategy.setInclude(new String[]{
                "article_base"
        });

        /**
         *设置表名前缀;
         *此表名前缀若与数据库表名前缀一致,则自动创建实体类时,实体类名匹配数据库表名前缀后面的名;
         *栗子:数据库表名为 article_base ,当strategy.setTablePrefix("article_")时,则实体类名为Base;
         *若不设置表名前缀,则实体类名匹配数据库表名
         */
        strategy.setTablePrefix(pc.getModuleName() + "_");

  • article_article_info
    • 为什么使用这个命名
      前面分析article_base这个命名时说到了,代码生成器的表名前缀若与数据库的表名前缀一致时,则生成的实体类名为表名前缀后的名;
      也就是说代码生成器的表名前缀为"article_",数据库表名为"article_info",则实际由代码生成器生成的实体类名为Info。而单单Info这个名并不够直观,我希望它是ArticleInfo,因此article_info前面再加了article,这样命名就是article_article_info,然后代码生成器生成的实体类名就是ArticleInfo,不过这样看着就很重复了。
    • 为什么抛弃这个命名
      抛弃的原因很简单,因为article_article_info这样的命名看着很重复,很不舒服,心里难受,就抛弃了。。
  • article_info
    • 为什么最终使用这个命名
      使用这个命名的原因也很简单,在抛弃了article,article_base,article_article_info这几个命名后,权衡利弊,综合考虑,还是觉得article_info这个命名好

命名规范这个坑我写得还挺长,我觉得能看完命名规范这个坑的都是勇者,我为你们鼓掌,毕竟看我在命名规范这里瞎BB也不容易。

纪二:数据库建立索引

在搞定命名规范问题后,又要面对索引问题。
在进行数次网上冲浪后,对怎么建立索引进行了简单总结。

  • 【推荐】对出现在SQL语句的WHERE后面的字段建立索引
  • 【推荐】对需要精确查询("=","IN"和"<=>"查询)的字段建立HASH索引
  • 【推荐】对需要范围查询的字段建立BTREE索引

好!总结完毕!可能有些人觉得太简单了,的确,相对于其他关于索引的文章,我总结的是很简单,所以仅仅只能适用于简单的情况,至于什么是简单的情况,请各位自己思考;
关于我参考的网上冲浪资料在文末的参考文章给出

纪三:模型同步到数据库中出现问题

Navicat Premuim中设计模型后同步到数据库中出现问题

【Spring Boot】搭建个人博客 - 后端环境搭建_第12张图片
模型同步到数据库失败

分析
因为我在某些字段上加了 HASH(哈希)索引,而 HASH索引是不支持排序的;而从模型同步到数据库时,建立了索引的字段默认用了ASC排序(升序排列),就算你建立索引时不设置排序方式,同步时也会默认用ASC排序,这有点坑。。而HASH索引不支持排序,自然同步会失败。

  • 模型中并没有设置排序方式


    【Spring Boot】搭建个人博客 - 后端环境搭建_第13张图片
    排序方式
  • 将模型导出为SQL文件,发现默认用了ASC排序


    【Spring Boot】搭建个人博客 - 后端环境搭建_第14张图片
    ASC排序

解决方法
将模型导出为SQL文件,再将SQL文件中的ASC去除,最后在Navicat Premium中运行SQL文件,完美解决!

后记

。。终于,我!写到了后记;实不相瞒,这篇文章,我也写了好久,期间历经修修改改,推翻重来之类的。下面就是我瞎BB的时间了;it's my show time!!

  • Q:别人的项目后端环境都搭建很快的,为什么你用了这么久!!
    A:你也说是别人了,我就是我,是与众不同的我;好吧,开玩笑的,主要是两点原因;
    其一:命名规范问题;没错,就是我在踩坑纪中也提到的命名规范问题。以前我搭建项目后端环境也是很快的,好吧。不过,那时我并没有考虑太多命名规范什么的,导致我后面再看自己的代码时,就觉得 这是谁写的代码,什么鬼啊!
    其二:数据库索引;没错,同样是在踩坑纪中也提到的索引问题;呀,怎么去建立索引,我也思考了好久。
    以上便是我后端环境搭建这么久的原因。其实并不是什么技术上的大问题

  • Q:就算是那两点原因,那也不用久啊,从你项目需求分析开始,到现在两个多月了都,黄花菜都凉了。
    A:行的吧,的确也是不用那么久。还有原因就是我在实习。。可能你想说这不是理由,好吧,不是就不是,但我也利用了每天上班前的一小时,下班后的两小时在做这个项目了。我在努力了,为什么还是这么慢呢,究其原因,我还是知道的,就是目前的我还是太弱鸡了,不够强啊。嗯,我不能停滞不前。
    再有就是时不时懒癌发作。。就没有然后了
    综上,弱鸡+间歇性懒癌发作=久

  • Q:拖了这么久,对甲方爸爸有没有什么影响?
    A:独秀同学,请你坐下,一看你就没认真看我的文章,我这是个人博客项目,甲方爸爸不是其他人,就是我自己,至于对我有没有影响,请看下一个QA部分。
    如果硬要说甲方爸爸是其他人的话,那这个其他人就是关注我这个博客项目的人;我看到评论区有人催更的时候,真是有点受宠若惊。
    当然要是有真.甲方爸爸或者是商业项目的话,我肯定不会拖这么久了。要是真拖这么久的话,你这是在玩火啊,少年

  • Q:拖了这么久,对你有没有什么影响?
    A:一开始我以为不会有什么影响,直到开始写这篇文章时,才意识到影响太大了。
    消极影响:开始写这篇文章时,肯定要梳理一下思路,然后我就发现隔了太久了,有些思路就不知道是怎么回事了;有些bug,踩过的坑也因为太久了就忘记了;虽然平时在遇到bug时,会进行速记,但时间久了,就不知道这速记记的是什么鬼。所以影响真的是很大,我以后应该不会拖这么久了。
    积极影响:说实话,我自己也没想到让我卡壳这么久的并不是什么技术大问题,而是命名之类的设计问题。
    突然想起很久之前,一位已经在外工作的师兄说的一句话,你不能仅仅是Coder,还要是Designer;感觉自己已经朝着Designer往前了一步

终于。。快要写完了,这篇文章写得真是又长又久,能看到这里的,我真心觉得你牛逼
最后说一句,因为文章我写的又长又久,也不想回头校对,所以要是文章中有出错的地方或者您有其他意见,请在评论区中赐教

参考文章

因为隔了太久了,而且这次参考的很多,有些参考文章就忘记收藏了,下面给出我收藏记的:
阿里巴巴Java开发手册 1.4.0
MySQL命名、设计及使用规范
数据库设计中的命名规范
字段类型与合理的选择字段类型
MySQL索引类型 btree索引和hash索引的区别
Mybatis-Plus代码生成器
Spring Boot干货系列:(十二)Spring Boot使用单元测试
Spring Boot中使用Swagger2构建强大的RESTful API文档
RESTful API 最佳实践

项目地址

Github:h-blog

小结

最后做个小结吧。。本来不用做的。拖得太久了。。我也很绝望啊
希望自己能吸取这次教训吧,下次不要拖这么久了。
再再说一次,要是文中有错误或者有其他意见,请私信我或者评论区留言

你可能感兴趣的:(【Spring Boot】搭建个人博客 - 后端环境搭建)