个人博客项目完工

//20201202 写在前面:历经大半个月,个人博客项目终于从零到落地了!!虽然难度不是很大,可能都比不上一个本科生的毕设,但是很久之前就想自己动手搭一个博客系统了,一直因为各种原因没有开张,所以今天的完成还是很有成就感的~

本文主要总结一下项目的流程、技术点、难点,希望对有想搭建个人博客的朋友有所帮助

ps:博客还是初期版本_暂定版本0.0.1趴(麻雀虽小但五脏俱全~),之后会根据功能和体验改进(说不定会开放多用户嘞)

 

 

正文部分


一、使用技术

  • 前端:Semantic UI

  • 后端:Spring Boot

  • 前后端联结:Thymeleaf

  • 数据库:MySQL8

  • 开发环境:IDEA(Win7下)

  • 生产环境:CentOS 7


二、模块组成

前端展示部分:

公共部分:

  • 导航栏模块

  • 底部信息模块

博客展示系统:

  • 首页

    • 主体部分

      • 博客展示模块

      • 部分分类展示模块

      • 部分标签展示模块

      • 最新推荐博客展示模块

  • 分类页面

    • 主体部分:

      • 分类展示模块

      • 博客内容展示模块

  • 标签页面

    • 主体部分:

      • 标签展示模块

      • 博客内容展示模块

  • 归档页面

    • 归档内容模块

  • 搜索页面

    • 搜索结果展示模块

  • 关于我页面

    • 信息展示主体部分

博客管理系统

公共部分:
  • 二级导航栏

各个页面不同部分
  • 登录页面

    • 信息输入框

  • 欢迎页面

  • 博客管理页面

    • 博客模糊搜索模块

    • 博客状态展示&&编辑模块

  • 标签管理页面

    • 标签展示&&编辑模块

  • 标签发布页面

    • 标签输入模块

  • 分类管理页面

    • 分类展示&&编辑模块

  • 分类发布页面

    • 分类输入模块

  • 博客发布页面

    • 博客输入模块

      • 标题输入

      • 类型选择

      • 主体输入

      • 分类选择模块

      • 标签选择模块

      • 首图链接选择模块

      • 摘要输入模块

      • 功能选择模块

后端逻辑部分:

SpringBoot大致框架如下:

  • model层(面相对象的各个实体类,用于连接数据库)

  • dao层(结合model层,连接数据库)

  • service层(实现各实体类操作接口,结合dao层获取并处理数据)

  • view层(对接前端,给前端页面渲染发送所需的数据)

  • 配置:

    • 分为两个配置,在统一的application.yml文件中进行切换,开发环境使用的dev配置,可以改动数据库结构,生产环境使用pro配置,不可以改动数据库结构,只能进行增删改查

      • dev配置

      • pro配置

    • 项目配置:保存在pom.xml文件中

  • model层:

    • Blog实体类

    • Comment实体类

    • Tag实体类

    • Type实体类

    • User实体类

  • dao层(使用JPA对接数据库):

    • Blog仓库

    • Comment仓库

    • Tag仓库

    • Type仓库

    • User仓库

  • service层:

    • 博客服务接口

    • 评论服务接口

    • 标签服务接口

    • 分类服务接口

    • 用户服务接口

  • view层

    • 管理系统View层:

      • Blog控制器

      • 登录控制器

      • 标签处理控制器

      • 分类处理控制器

    • 展示系统View层:

      • 关于我页面控制器

      • 归档页面控制器

      • 评论功能控制器

      • 首页控制器

      • 标签展示控制器

      • 分类展示控制器

  • 工具类:

    • markdown文本处理(每次加载渲染html标签)类

    • MD5加密工具(用于登入用户密码加密,后台数据库中存储的是密文)

    • Bean工具类(用于比对更新数据与原有数据中不同的部分)

  • 拦截器:拦截未经许可访问管理页面的请求

  • 日志输出模块:

    • 自动写入日志模块

    • 异常拦截模块(防止控制器异常发生之后直接阻塞服务导致其他用户无法访问,将异常全部写入日志文件)


三、具体实现

ps:由于整体代码文件过长,所以代码就放在github上——之后的更新也会在github上说明,本文只做简略以及重点说明

 

源码在此:https://github.com/Lavender0423/blog

前端部分

管理系统:

技术点:
  • 使用thymeleaf模板中的fragment功能,设置公共fragment文件,将所有页面相同的部分放在fragment文件中,然后使用thymeleaf模板里的方法来获取;因为篇幅不长,此处贴一下其源码(导航栏使用了选择器来将当前页面高亮——需要在每个页面中传入不同的值)

  • <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head th:fragment="head(title)">
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width,initial-scale=1.0">
       <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui-css@2.4.1/semantic.min.css">
       <link rel="stylesheet" href="../static/css/blog.css"th:href="@{/css/blog.css}">
       <link rel="stylesheet" href="../../static/lib/editormd/css/editormd.min.css" th:href="@{/lib/editormd/css/editormd.min.css}">
       <title th:replace="${title}"></title>
    </head>
    <body>
    <nav th:fragment="nav(n)" class="ui inverted attached segment m-padded-tb-mini m-shadow-small" >
       <div class="ui container">
           <div class="ui inverted secondary stackable menu">
               <h2 class="ui teal header item">博客管理</h2>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/admin/blog_management}" th:classappend="${n==1}? 'active'"><i class="home icon"></i> 博客</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/admin/types}" th:classappend="${n==2}? 'active'"><i class=" idea icon"></i> 分类</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/admin/tags}" th:classappend="${n==3}? 'active'"><i class=" tags icon"></i> 标签</a>
               <div class="right m-item menu ">
                   <div class="ui dropdown item">
                       <div class="text">
                           <img src="https://unsplash.it/100/100?image=456" alt="" class="ui avatar image">
                          Lavender
                       </div>
                       <i class="dropdown icon"></i>
                       <div class="menu">
                           <a href="#" th:href="@{/admin/logout}" class="item">注销</a>
                       </div>
                   </div>
               </div>
           </div>
       </div>
       <a href="#" class="ui menu toggle button black icon m-top-right m-mobile-show">
           <i class="sidebar icon"></i>
       </a>
    </nav>


    <footer th:fragment="footer" class="ui inverted vertical segment m-padded-tb-massive">
       <div class = "ui center aligned container">
           <div class="ui inverted divided stackable grid">
               <div class = "three wide column">
                   <img src="../static/img/info/we_chat_QC_code.png" th:src="@{/img/info/we_chat_QC_code.png}" class="ui rounded centered image" alt="" style="width:110px">

               </div>
               <div class = "three wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">最新博客</h4>
                   <div class="ui inverted link list">
                       <a href="#" class="item">用户故事(User Story)</a>
                       <a href="#" class="item">用户故事</a>
                       <a href="#" class="item">用户故事</a>
                   </div>
               </div>
               <div class = "three wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">联系我</h4>
                   <div class="ui inverted link list">
                       <a href="#" class="item">Email:473243887@qq.com</a>
                       <a href="#" class="item">QQ:473243887</a>

                   </div>
               </div>
               <div class = "seven wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">Lavender</h4>
                   <p class="m-text-thin m-text-spaced m-opacity-mini">这是我的个人博客,会分享关于我生活、学习的点点滴滴,希望能给来到本站的你们有所帮助</p>
               </div>
           </div>
           <div class="ui inverted section divider"></div>
           <p class="m-text-thin m-text-spaced m-opacity-tiny">Copyright © 2012 - 2020 Lavender Designed by Pansy</p>
       </div>
    </footer>
    <th:block th:fragment="script">
       <script src="https://cdn.jsdelivr.net/npm/jquery@3.2/dist/jquery.min.js"></script>
       <script src="https://cdn.jsdelivr.net/npm/semantic-ui-css@2.4.1/semantic.min.js"></script>
       <script src="../../static/lib/editormd/editormd.min.js" th:src="@{/lib/editormd/editormd.min.js}"></script>
    </th:block>
    </body>
    </html>

  • 在有输入框的地方均加上了前端第一层验证(JQuery实现),如果不符合格式或者空白,则无法正常提交,会跳出提醒框,示例代码如下

  • $('.ui.form').form({
          fields :{
              title : {
                  identifier:'title',
                  rules:[{
                      type:'empty',
                      prompt:'标题:请输入博客标题'
                  }]
              },
              content:{
                  identifier:'content',
                  rules:[{
                      type:'empty',
                      prompt:'内容:请输入博客内容'
                  }]
              },
              type : {
                  identifier:'type.id',
                  rules:[{
                      type:'empty',
                      prompt:'分类:请选择分类'
                  }]
              },
              firstpicture : {
                  identifier:'firstpicture',
                  rules:[{
                      type:'empty',
                      prompt:'首图:请输入首图地址链接'
                  }]
              },
              description : {
                  identifier:'description',
                  rules:[{
                      type:'empty',
                      prompt:'博客描述:请输入博客描述'
                  }]
              },
          }
      });
  • 博客输入界面中,使用commonmark组件直接生成文本输入框(可动态展示markdown文本)

    • 引入资源:

      • <link rel="stylesheet" href="../../static/lib/editormd/css/editormd.min.css">

      • <script src="../../static/lib/editormd/editormd.min.js"></script>

    • 组件生成代码

    • <div class="required field">
         <div id="md-content" style="z-index:1!important;">
              <textarea placeholder="Enjoy you coding now..."th:text="*{content}" name="content" style="display:none;"></textarea>
         </div>
      </div>

      <style>
      var contentEditor;

        $(function() {
             contentEditor = editormd("md-content", {
                 width: "100%",
                 height: 640,
                 syncScrolling: "single",
                // path: "../static/lib/editormd/lib/"
                 path: "/lib/editormd/lib/"
            });
        });
      </style>

前端展示系统:

  • 类似于管理系统,也使用了thymeleaf模板中的fragment功能,源码如下

  • <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head th:fragment="head(title)">
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width,initial-scale=1.0">
       <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui-css@2.4.1/semantic.min.css">
       <link rel="stylesheet" href="../static/css/typo.css" th:href="@{/css/typo.css}">
       <link rel="stylesheet" href="../static/css/animate.css" th:href="@{/css/animate.css}">
       <link rel="stylesheet" href="../static/lib/prism/prism.css" th:href="@{/lib/prism/prism.css}">
       <link rel="stylesheet" href="../static/lib/tocbot/tocbot.css"th:href="@{/lib/tocbot/tocbot.css}">
       <link rel="stylesheet" href="../static/css/blog.css"th:href="@{/css/blog.css}">
       <title th:replace="${title}"></title>
    </head>
    <body>
    <nav th:fragment="nav(n)" class="ui inverted attached segment m-padded-tb-mini m-shadow-small" >
       <div class="ui container">
           <div class="ui inverted secondary stackable menu">
               <h2 class="ui teal header item">Blog</h2>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/}" th:classappend="${n==1}? 'active'"><i class="home icon"></i> 首页</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/types/-1}" th:classappend="${n==2}? 'active'"><i class=" idea icon"></i> 分类</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/tags/-1}" th:classappend="${n==3}? 'active'"><i class=" tag icon"></i> 标签</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/archives}" th:classappend="${n==4}? 'active'"><i class=" clone icon"></i> 归档</a>
               <a href="#" class="m-item m-mobile-hide item" th:href="@{/about_me}" th:classappend="${n==5}? 'active'"><i class=" info icon"></i> 关于我</a>
               <div class="right item">
                   <form name="search" method="post" action="#" th:action="@{/search}" target="_blank">
                       <div class="ui icon inverted transparent input">
                           <input name="query" type="text" placeholder="Search..." th:value="${query}">
                           <i onclick="document.forms['search'].submit()" class="search link icon"></i>
                       </div>
                   </form>
               </div>
           </div>
       </div>
       <a href="#" class="ui menu toggle button black icon m-top-right m-mobile-show">
           <i class="sidebar icon"></i>
       </a>
    </nav>

    <footer th:fragment="footer" class="ui inverted vertical segment m-padded-tb-massive">
       <div class = "ui center aligned container">
           <div class="ui inverted divided stackable grid">
               <div class = "three wide column">
                   <img src="../static/img/info/we_chat_QC_code.png" th:src="@{/img/info/we_chat_QC_code.png}" class="ui rounded centered image" alt="" style="width:110px">

               </div>
               <div class = "three wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">最新博客</h4>
                   <div id="newblog-container">
                       <div class="ui inverted link list" th:fragment="newblogList">
                           <a href="#" class="item"th:href="@{/Blog/{id}(id=${blog.id})}" target="_blank"th:each="blog : ${newblogs}" th:text="${blog.title}">dh</a>

                       </div>
                   </div>
               </div>
               <div class = "three wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">联系我</h4>
                   <div class="ui inverted link list">
                       <span class="item" th:text="|Email: #{index.email}|"></span>
                       <span class="item" th:text="|QQ: #{index.qq}|"></span>

                   </div>
               </div>
               <div class = "seven wide column">
                   <h4 class="ui inverted header m-text-thin m-text-spaced m-opacity-mini">Lavender</h4>
                   <p class="m-text-thin m-text-spaced m-opacity-mini">这是我的个人博客,会分享关于我生活、学习的点点滴滴,希望能给来到本站的你们有所帮助</p>
               </div>
           </div>
           <div class="ui inverted section divider"></div>
           <p class="m-text-thin m-text-spaced m-opacity-tiny">Copyright © 2012 - 2020 Lavender Designed by Pansy</p>
       </div>
    </footer>
    <th:block th:fragment="script">
       <script src="https://cdn.jsdelivr.net/npm/jquery@3.2/dist/jquery.min.js"></script>
       <script src="https://cdn.jsdelivr.net/npm/semantic-ui-css@2.4.1/semantic.min.js"></script>
       <script src="../static/lib/prism/prism.js" th:src="@{/lib/prism/prism.js}"></script>
       <script src="../static/lib/tocbot/tocbot.js" th:src="@{/lib/tocbot/tocbot.js}"></script>
       <script src="https://cdn.jsdelivr.net/npm/jquery.scrollto@2.1.2/jquery.scrollTo.min.js"></script>
       <script src="../static/lib/qrcode/qrcode.js" th:src="@{/lib/qrcode/qrcode.js}"></script>
       <script src="../static/lib/waypoints/jquery.waypoints.js" th:src="@{/lib/waypoints/jquery.waypoints.js}"></script>
       <script>
           $('#newblog-container').load(/*[[@{/footer/newblog}]]*/"/footer/newblog");
       </script>
    </th:block>
    </body>
    </html>
  • 前端展示页面中的个人介绍界面为静态界面

  • 博客展示页面与个人介绍页面中都有二维码跳出元素

    • 博客展示页面:

      • 赞赏按钮点击之后支付宝和微信二维码会跳出

      • 右方工具条中微信图标点击时会产生该网址二维码

      • 跳出二维码实现如下代码块

      • $('.button.wechat').popup({
               popup:$('#QR-code'),
               position:'left center'
          });

        $('#payButton').popup({
               popup:$('.payQR.popup'),
               on:'click' ,
               position:'bottom center'
          });
  • 前端所有页面都做成了响应式的,可响应手机小屏幕,小屏幕会收起导航栏,变成菜单按钮,点击之后会蹦出所有图标

  • 归档页面的日期从新到旧,归档基于创建时间而非更新时间

  • 评论展示采用二级目录格式(最多二级,超出二级全部扁平化为二级),源码放在后端介绍里,评论里博主id前面会有“博主”字样tag,回复评论会有@标记

  • 前端展示页面均加上了animate样式,页面主体部分会延时出现

后端部分

dao层

  • 除了各个接口普通的方法外,还有如下自定义方法及语句:

    • BlogRepository中:

    • @Query("select b from Blog b where b.recommend = true ")
       List<Blog> findNew(Pageable pageable);

       // 1 indicates parameter 1,2 indicates parameter 2
       @Query("select b from Blog b where b.title like ?1 or b.content like ?1")
       Page<Blog> findByQuery(String query, Pageable pageable);

       @Transactional
       @Modifying
       @Query("update Blog b set b.views = b.views+1 where b.id = ?1")
       int updateViews(Long id);

       @Query("select function('date_format',b.updateTime,'%Y') as year from Blog b group by function('date_format',b.updateTime,'%Y') order by function('date_format',b.updateTime,'%Y') desc ")
       List<String> findGroupByYear();

       @Query("select b from Blog b where function('date_format',b.createTime,'%Y') = ?1")
       List<Blog> findByYear(String year);
    • CommentRepository中

    • //符合Repository书写规范,自动转化为SQL语句
      List<Comment> findByBlogIdAndParentCommentNull(Long blogId, Sort sort);

        // only get one
        @Query(value="select * from t_comment c where c.nickname = ?1 limit 0,1",nativeQuery = true)
        Comment findByNickname(String nickname);
    • TagRepository中

    • //符合Repository书写规范,自动转化为SQL语句
      Tag findByName(String name);

       @Query("select t from Tag t")
       List<Tag> findTop(Pageable pageable);
    • TypeRepository中

    • //符合Repository书写规范,自动转化为SQL语句
       	Type findByName(String name);
       
         @Query("select t from Type t")
         List<Type> findTop(Pageable pageable);
    • UserRepository中

    •     User findByUsernameAndPassword(String username,String password);
  • service层

    • BlogService:主要工作有

      • 从dao层获取博客数据

      • 获取符合查询条件(模糊查询)的博客数据

      • 将markdown格式文本转化成html格式的标签化数据

      • 获取固定数量的博客(根据博客推荐时间排序)

      • 删除博客数据

      • 更新博客数据

      • 统计博客数量

      • 保存博客数据

    • CommentService:主要工作有:

      • 获取评论数据

      • 解析父子级评论关系并扁平化

    • 其余Service无非增删改查,就不再赘述,详情请看源码

  • view层

    • 都是一些很寻常的操作,就在此说一下需要注意的点

    • 在type和tag的view层,初始化的时候传回的id默认是-1,需要在该层判断并返回默认值,否则会出错

    • 在评论的view层,使用service层连接dao层进入数据库根据名称查询用户信息,相同名称的生成相同头像(使用随机图像网站链接,id使用random函数随机生成)

    • 有些页面中只需要部分渲染,此处使用fragments :: newblogList类似的格式来返回片段信息,这样就防止获取部分数据而整个页面刷新(增强用户体验)

  • 拦截器:

    • 拦截未经许可访问管理系统的请求,源码如下:

    • @Override
          public void addInterceptors(InterceptorRegistry registry) {
              //设置拦截所有admin路径下的请求
              registry.addInterceptor(new LoginInterceptor())
              .addPathPatterns("/admin/**")
              .excludePathPatterns("/admin")
              .excludePathPatterns("/admin/login");
      
          }
    • 拦截view层产生的所有异常,并阻止其挂起服务器而是将其写入日志文件中,源码如下:

    • //    获取日志对象
          private final Logger logger = LoggerFactory.getLogger(this.getClass());
      
      //    表示此方法是用于异常处理的
          @ExceptionHandler(Exception.class)
          public ModelAndView exceptionHandler(HttpServletRequest request,Exception e) throws Exception{
      //        获取并记录异常
              logger.error("Request URL : {}.Exception : {}",request.getRequestURL(),e);
              if(AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null){
                  throw e;
              }
              ModelAndView mv = new ModelAndView();
              mv.addObject("url",request.getRequestURL());
              mv.addObject("exception",e);
              mv.setViewName("error/error");
      
              return mv;
          }
  • 配置

    • 配置分为开发环境配置与生产环境配置

    • dev(开发环境)配置源码:

    • #开发环境
      #数据库
      spring:
        datasource:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://localhost:3306/blog?useUnicode=true&charaterEncoding=utf-8&serverTimezone=UTC
          username: root
          password: 123456
        jpa:
      #    在开发环境可以更新数据库
          hibernate:
            ddl-auto: update
          show-sql: true
      #日志
      logging:
        level:
          root: info
          com.lavender.Blog: debug
        file:
          name: log/blog-dev.log
    • pro(生产环境配置):

    • #生产环境
      #数据库
      spring:
        datasource:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://localhost:3306/blog?useUnicode=true&charaterEncoding=utf-8&serverTimezone=UTC
          username: root
          password: *******
          # 已打码
        jpa:
      #    在生产环境禁止改动数据库
          hibernate:
            ddl-auto: none
          show-sql: true
      #日志
      logging:
        level:
          root: warn
          com.lavender.Blog: info
        file:
          name: log/blog-pro.log
      server:
        port: 8080
    • 二者区别:

      • 开发环境允许改动数据库结构,生产环境不允许,只支持增删改查

      • 输出信息不一样,开发环境输出debug级别信息,而生产环境输出info级别信息(会少很多)

    • 另有一个总配置文件来控制环境(active中填入的环境即为激活环境),代码如下:

    • #数据库
      spring:
        thymeleaf:
          mode: HTML
      #    指定使用配置
        profiles:
          active: dev

项目总依赖配置(maven)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.lavender</groupId>
	<artifactId>Blog</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Blog</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
		<thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>com.atlassian.commonmark</groupId>
			<artifactId>commonmark</artifactId>
			<version>0.15.2</version>
		</dependency>
		<dependency>
			<groupId>com.atlassian.commonmark</groupId>
			<artifactId>commonmark-ext-gfm-tables</artifactId>
			<version>0.15.2</version>
		</dependency>
		<dependency>
			<groupId>com.atlassian.commonmark</groupId>
			<artifactId>commonmark-ext-heading-anchor</artifactId>
			<version>0.15.2</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.22.2</version>
				<configuration>
					<skipTests>true</skipTests>
				</configuration>
			</plugin>

		</plugins>

		<resources>
			<resource>
				<!-- 指定resources插件处理哪个目录下的资源文件 -->
				<directory>${basedir}/src/main/webapp</directory>
				<!-- 需要将资源文件放到该目录下才能访问 -->
				<targetPath>META-INF/resources</targetPath>
				<includes>
					<include>**/**</include>
				</includes>
			</resource>

			<resource>
				<directory>${basedir}/src/main/resources</directory>
			</resource>
		</resources>
	</build>
	<packaging>jar</packaging>
	<repositories>
		<repository>
			<id>alimaven</id>
			<url>https://maven.aliyun.com/repository/public</url>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>alimaven</id>
			<url>https://maven.aliyun.com/repository/public</url>
		</pluginRepository>
	</pluginRepositories>

</project>

四、遇到的问题&解决方法

前端篇:

  • 跳出二维码被底部信息栏挡住------->解决方法:设置跳出二维码z-index,让其比底部信息栏配置高

  • 模板渲染一定要和view层中的参数一致,否则会报错

后端篇:

  • 使用annotation @Query设置JPA自定义语句时,如果是MySQL专有语法,比如‘limit’,需要将语句赋值给value,再设置参数“nativeQuery ”为 true------------->意义为不在java中处理语句,将语句直接交给MySQL执行,然后解析对应返回对象,因为java中不会解析,所以选择数据库表需要使用原名而不是model层中的实体类名,后者将会报错

  • Spring(2.1.1)下PageRequest、Sort类不再支持直接实例化对象,需要使用静态方法获取对象----------------->源码如下

  • @Override
        public List<Type> listTypeTop(Integer size) {
            Sort sort = Sort.by(Sort.Direction.DESC,"blogs.size");
            Pageable pageable = PageRequest.of(0,size,sort);
            return typeRepository.findTop(pageable);
        }

配置篇:

  • 在将项目部署到云端时,一定要将生产环境的数据库配置成云端数据库的数据,且需要先在云端设计好数据库或者在云端线性运行一遍开发环境创建好数据库表,否则会报错

部署篇:

  • 首先需要安装和开发环境相同版本的数据库(更加保险,避免改代码)

  • 云端需要开放8080端口(安全组以及防火墙_此处使用的是阿里云学生服务器)

  • 设置脚本文件来控制服务器的运行&停止&重启(一共四个脚本文件),源码如下

    • 运行(在jar包所在目录下运行):

    • java -jar Blog-0.0.1-SNAPSHOT.jar
    • 不间断运行——直接运行startup.sh,在控制终端关闭之后,服务便会停止,此时需要使用nohup指令(不挂起)来不间断运行以保持终端关闭之后服务器状态,源码如下

    • nohup ./startup.sh
    • 关闭服务器_大致逻辑为:使用ps查询所有进程---->使用管道输出到grep来查询带有运行指令相关的进程----->使用管道输出到grep -v来删除带有grep的进程---->最后通过awk来获取进程pid------>最后使用kill -9杀死进程,源代码如下

    • echo "---------find Blog process----------"
      process=`ps -ef | grep 'java -jar Blog-0.0.1-SNAPSHOT.jar' | grep -v 'grep'`
      echo "process is :"
      echo "$process"
      id=`ps -ef | grep 'java -jar Blog-0.0.1-SNAPSHOT.jar' | grep -v 'grep' | awk '{print $2}'`
      echo "this operation will kill the process which id is $id"
      kill -9 $id
      echo "done"
      echo "-----------opearation finished------"
    • 重启服务器:先执行shutdown再执行startup

    • ./shutdown.sh
      
      ./nohup-starup.sh
  • 使用nohup指令后,输出会全部保存到nohup.out文件中,如果访问量过大,会让该文件毫无逻辑且过于庞大,可以将nohup指令输出注入到dev/null(黑洞)之中_未实施,目前访问量较小,待更新...


五、 Summary

  • 博客大概逻辑:

  • 前端展示---->发送请求给view层----->view层调用Service层获取数据----->Service层根据所需连接Repository获取数据----->dao层直接从数据库中取出数据------>返回前端页面渲染(后期补上流程图,此处略显简略)

 

以上

希望对大家有所帮助

posted @ 2020-12-02 22:35  醉生梦死_0423  阅读(276)  评论(0编辑  收藏  举报