Spring实战项目【从0到1】:博客系统(上)

1. 项目介绍

使用SSM框架(Spring、Spring MVC、MyBatis框架)实现⼀个简单的博客系统共5个页面:

  1. 用户登录
  2. 博客发表页
  3. 博客编辑页
  4. 博客列表页
  5. 博客详情页

功能描述:
用户登录成功后,可以查看所有⼈的博客。点击 <<查看全文>> 可以查看该博客的正⽂内容。如果该博客作者为当前登录用户,可以完成博客的修改和删除操作,以及发表新博客。
页面预览:
用户登录页面:
image.png
博客列表页:
image.png
博客详情页:
image.png
博客编辑页:
image.png

2. 项目准备

2.1 数据库准备

-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;

-- 用户表
DROP TABLE IF EXISTS java_blog_spring.user;
CREATE TABLE java_blog_spring.user(
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 128 ) NOT NULL,
`password` VARCHAR ( 128 ) NOT NULL,
`github_url` VARCHAR ( 128 ) NULL,
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id ),
UNIQUE INDEX user_name_UNIQUE ( user_name ASC )) ENGINE = INNODB DEFAULT
CHARACTER
SET = utf8mb4 COMMENT = '用户表';

-- 博客表
drop table if exists java_blog_spring.blog;
CREATE TABLE java_blog_spring.blog (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NULL,
`content` TEXT NULL,
`user_id` INT(11) NULL,
`delete_flag` TINYINT(4) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY (id))
ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';

-- 新增用户信息
insert into java_blog_spring.user (user_name,password,github_url)
values("zhangsan","123456","https://gitee.com/little-fishs-code-house/java-ee2");
insert into java_blog_spring.user (user_name,password,github_url)
values("lisi","123456","https://gitee.com/little-fishs-code-house/java-ee2");
insert into java_blog_spring.blog (title,content,user_id) 
values("第⼀篇博客","1我是博客正我是博客正文,这是我的第一篇博客",1);
insert into java_blog_spring.blog (title,content,user_id) 
values("第⼆篇博客","2我是博客正文我是博客正文,这是我的第二篇博客",2);

image.png
image.png

2.2 创建项目

创建SpringBoot项目, 添加Spring MVC 和MyBatis对应依赖。
image.png

2.3 配置文件

在application.yml文件中配置数据库相关的信息:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=false
    username: root
    password: '0124'
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #配置打印sql语句
    map-underscore-to-camel-case: true #配置驼峰自动转换
  mapper-locations: classpath:mapper/**Mapper.xml
#设置日志文件的文件名
logging:
  file:
    name: spring-blog.log

2.4 准备前端页面

把博客系统静态页⾯拷贝到static目录下:
image.png

2.5 测试

访问前端页面:http://127.0.0.1:8080/blog_login.html
image.png
前端页面可以正常显示,说明项目初始化成功。

3. 项目公共模块

项⽬分为控制层(Controller), 服务层(Service), 持久层(Mapper)。各层之间的调⽤关系如下:
image.png
我们先根据需求完成实体类和公共层代码的编写。

3.1 实体类

@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private String githubUrl;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}
@Data
public class BlogInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer userId;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

3.2 公共层

统一功能处理:

  1. 拦截器
  2. 统一结果返回
  3. 统一异常处理

拦截器内容在后面用户登录时介绍,这部分我们先写统一结果返回和统一异常处理。

  1. 统一结果返回为实体类:
    1. code:业务状态码
      • 200:业务处理成功
      • -1:业务处理失败
      • -2:用户未登录
    2. errMsg:业务处理失败时,返回的错误信息
    3. data:业务返回的数据

定义业务状态码:

public class Constant {
    public final static Integer SUCCESS_CODE = 200;// 成功
    public final static Integer FAIL_CODE = -1;// 失败
    public final static Integer UNLOGIN_CODE = -2;// 未登录
}
/**
 * 定义接口的统一返回结果
 */
@Data
public class Result {
    private int code;// 业务码  定义:200-成功  -1-失败  -2-未登录
    private String errMsg;// 接口发生错误的信息
    private Object data;// 接口的返回真实结果

    /**
     * 接口返回成功时
     */
    public static Result success(Object data) {
        Result result = new Result();
        result.setCode(Constant.SUCCESS_CODE);
        result.setErrMsg("");
        result.setData(data);
        return result;
    }

    /**
     * 接口返回失败时
     */
    public static Result fail(String errMsg) {
        Result result = new Result();
        result.setCode(Constant.FAIL_CODE);
        result.setErrMsg(errMsg);
        result.setData(null);
        return result;
    }

    public static Result fail(String errMsg, Object data) {
        Result result = new Result();
        result.setCode(Constant.FAIL_CODE);
        result.setErrMsg(errMsg);
        result.setData(data);
        return result;
    }

    /**
     * 用户未登录时
     */
    public static Result unLogin(String errMsg) {
        Result result = new Result();
        result.setCode(Constant.UNLOGIN_CODE);
        result.setErrMsg("用户未登录");
        result.setData(null);
        return result;
    }
}
  1. 统一返回结果:
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 对于哪些方法执行统一结果返回,可以自己定义,不写默认所有方法
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 统一结果返回的具体逻辑
        if (body instanceof Result) {
            return body;
        }
        // 如果接口返回为String类型,进行单独处理
        if (body instanceof String) {
            // 转为JSON类型
//            ObjectMapper objectMapper = new ObjectMapper();// 也可以直接注入进来
            return objectMapper.writeValueAsString(Result.success(body));
        }

        return Result.success(body);
    }
}
  1. 统一异常处理:
@ResponseBody
@ControllerAdvice
public class ErrorHandler {

    @ExceptionHandler
    public Result handler(Exception e) {
        return Result.fail(e.getMessage());
    }

    // 两种写法
//    @ExceptionHandler(NullPointerException.class)
//    public Result handler(Exception e) {
//        return Result.fail(e.getMessage());
//    }
//
//    @ExceptionHandler
//    public Result handler(NullPointerException e) {
//        return Result.fail(e.getMessage());
//    }
}

4. 业务代码

4.1 持久层代码

根据需求, 先⼤致计算有哪些DB相关操作, 完成持久层初步代码, 后续再根据业务需求进⾏完善。
每个页面需要实现的接口:

  1. 用户登录页
    1. 用户登录:根据用户名和密码,判断是否正确。

具体实现:根据用户名,查找用户信息,对比密码是否正确。
DB操作:根据用户名,查询用户信息。

  1. 博客列表页
    1. 查询用户信息:根据用户ID,查询用户信息。

DB操作:根据用户ID,查询用户信息。

  1. 获取所有博客列表:查询所有博客。

DB操作:查询所有博客。

  1. 博客详情页
    1. 查询作者信息
      1. 根据博客,拿到作者ID
      2. 根据作者ID,获取作者信息

DB操作:根据用户ID,查询用户信息。

  1. 查询博客详情:根据博客ID,查询博客信息

DB操作:根据博客ID,查询博客信息。

  1. 删除博客:根据博客ID,删除博客(修改delete_flag=1)

DB操作:根据博客ID,修改博客信息。

  1. 博客编辑页
    1. 修改博客:根据博客ID,修改博客信息。

DB操作:根据博客ID,修改博客信息。

  1. 发表博客:添加新的博客信息。

DB操作:插入新的博客数据。
总结
用户表

  1. 根据用户名,查询用户信息
  2. 根据用户ID,查询用户信息

博客表

  1. 查询博客列表
  2. 根据博客ID,查询博客信息
  3. 根据博客ID,修改博客信息
  4. 插入博客

根据以上分析, 来实现持久层的代码:

@Mapper
public interface UserMapper {
    // 根据用户名,查找用户信息
    // *可以替换成具体字段
    @Select("select * from user where user_name = #{userName} and delete_flag = 0")
    UserInfo selectByName(String userName);

    // 根据用户ID,查询用户信息
    @Select("select * from user where id = #{userId} and delete_flag = 0")
    UserInfo selectById(Integer userId);
}
@Mapper
public interface BlogMapper {
    // 查询博客列表
    @Select("select * from blog where delete_flag = 0 order by create_time desc")
    List<BlogInfo> selectAllBlog();

    // 根据博客Id,查询博客信息
    @Select("select * from blog where id = #{blogId} and delete_flag = 0")
    BlogInfo selectById(Integer blogId);

    // 根据博客ID,修改博客信息(包含修改和删除,根据参数决定修改什么)
    Integer updateBlog(BlogInfo blogInfo);

    // 插入博客
    @Insert("insert into blog (title, content, user_id) values(#{title}, #{content}, #{userId})")
    Integer insertBlog(BlogInfo blogInfo);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaoyu.blog.mapper.BlogMapper">
    <update id="updateBlog">
        update blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="content != null">
                content = #{content},
            </if>
            <if test="deleteFlag != null">
                delete_flag = #{deleteFlag}
            </if>
        </set>
        where id = #{id}
    </update>
</mapper>

书写测试用例, 简单进行单元测试:

@SpringBootTest
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    void selectByName() {
        System.out.println(userMapper.selectByName("zhangsan"));
    }

    @Test
    void selectById() {
        System.out.println(userMapper.selectById(2));
    }
}

运行程序,观察日志:

  1. selectByName接口返回结果:

image.png

  1. selectById接口返回结果:

image.png
接口返回均正确。

@SpringBootTest
class BlogMapperTest {
    @Autowired
    private BlogMapper blogMapper;

    @Test
    void selectAllBlog() {
        System.out.println(blogMapper.selectAllBlog());
    }

    @Test
    void selectById() {
        System.out.println(blogMapper.selectById(1));
    }

    @Test
    void updateBlog() {
        BlogInfo blogInfo = new BlogInfo();
        blogInfo.setTitle("update更新标题");
        blogInfo.setContent("update更新内容");
        blogInfo.setId(1);
        System.out.println(blogMapper.updateBlog(blogInfo));
    }

    @Test
    void deleteBlog() {
        BlogInfo blogInfo = new BlogInfo();
        blogInfo.setId(2);//第二条数据
        blogInfo.setDeleteFlag(1);
        System.out.println(blogMapper.updateBlog(blogInfo));
    }

    @Test
    void insertBlog() {
        BlogInfo blogInfo = new BlogInfo();
        blogInfo.setTitle("insert第三篇博客");
        blogInfo.setContent("insert博客内容");
        blogInfo.setUserId(2);
        System.out.println(blogMapper.insertBlog(blogInfo));
    }
}
  1. 测试接口selectAllBlog,观察日志:

image.png

  1. 测试接口selectById,观察日志:

image.png

  1. 测试接口updateBlog,观察日志:

image.png
更新前数据库信息为:
image.png
更新后数据库信息为:
image.png

  1. 测试接口deleteBlog,观察日志:

image.png
更新后数据库信息为:
image.png

  1. 测试接口insertBlog,观察日志:

image.png
更新后数据库信息为:
image.png
接口均测试成功。

4.2 实现博客列表

约定前后端交互接口:

[请求]
/blog/getList
[响应]
{
    "code": 200,
    "errMsg": "",
    "data": [
        {
            "id": 4,
            "title": "insert第三篇博客",
            "content": "insert博客内容",
            "userId": 2,
            "deleteFlag": 0,
            "createTime": "2024-04-26T10:49:52.000+00:00",
            "updateTime": "2024-04-26T10:49:52.000+00:00"
        },
        ...
    ]
}

客户端给服务器发送⼀个 /blog/getList 这样的 HTTP 请求, 服务器给客户端返回了⼀个 JSON 格式的数据。
实现服务器代码:

@RestController
@RequestMapping("/blog")
public class BlogController {
    @Autowired
    private BlogService blogService;

    @RequestMapping("/getList")
    public List<BlogInfo> queryBlogList() {
        return blogService.queryBlogList();
    }
}
@Service
public class BlogService {
    @Autowired
    private BlogMapper blogMapper;

    public List<BlogInfo> queryBlogList() {
        return blogMapper.selectAllBlog();
    }
}

启动程序,验证服务器能否返回正确数据:访问http://127.0.0.1:8080/blog/getList,响应结果为:
image.png
实现客户端代码:
我们希望访问blog_list.html这个页面,在页面加载的时候就去调用后端接口,返回数据,并进行填充,所以修改blog_list.html,删除之前写死的博客内容(即 <divclass=“blog”> ),并新增 js 代码处理ajax 请求。

  • 使用 ajax 给服务器发送 HTTP 请求。
  • 服务器返回的响应是⼀个 JSON 格式的数据, 根据这个响应数据使用 DOM API 构造页⾯内容。
  • 响应中的 createTime 字段为 ms 级时间戳, 需要转成格式化日期。
  • 跳转到博客详情页的 url 形如 blog_detail.html?blogId=1 这样就可以让博客详情页知道当前是要访问哪篇博客。
<script>
        $.ajax({
            type: "get",
            url: "/blog/getList",
            success: function(result) {
                // 如果result.code == 200 && result.data == null,页面可以提示:当前还没有任何博客,快去写博客吧!并进行页面跳转
                if (result.code == 200 && result.data != null) {
                    var finalHtml = "";
                    // 页面展示
                    // 循环拼接result.data里面的数据
                    for (var blog of result.data) {
                        finalHtml += '<div class="blog">';
                        finalHtml += '<div class="title">'+ blog.title +'</div>';
                        finalHtml += '<div class="date">'+ blog.createTime +'</div>';
                        finalHtml += '<div class="desc">'+ blog.content +'</div>';
                        finalHtml += '<a class="detail" href="blog_detail.html?blogId='+ blog.id +'">查看全文&gt;&gt;</a>';
                        finalHtml += '</div>';
                    }
                    $(".right").html(finalHtml);
                }
            }
        });
    </script>

运行程序,通过url:http://127.0.0.1:8080/blog_list.html访问服务器:
image.png
我们发现页面的日期显示为ms 级时间戳, 需要转成格式化日期,从后端对日期进行处理:
SimpleDateFormat 格式参考官⽅⽂档:
image.png

/**
 * 日期工具类
 */
public class DateUtils {

    public static String formatDate(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        return simpleDateFormat.format(date);
    }
}

修改BlogInfo实体类:
image.png
重新启动程序,通过 URL http://127.0.0.1:8080/blog_list.html 访问服务器, 验证效果:
image.png

4.3 实现博客详情

⽬前点击博客列表⻚的 “查看全⽂” , 能进⼊博客详情⻚, 但是这个博客详情⻚是写死的内容。我们期望能够根据当前的博客 id 从服务器动态获取博客内容。
约定前后端交互接口:

[请求]
/blog/getBlogDetail?blogId=1
[响应]
{
  "code": 200,
  "msg": "",
  "data": {
    "id": 1,
    "title": "第⼀篇博客",
    "content": "111我是博客正⽂我是博客正⽂我是博客正⽂",
    "userId": 1,
    "deleteFlag": 0,
    "createTime": "2023-10-21 16:56:57",
    "updateTime": "2023-10-21T08:56:57.000+00:00"
  }
}

实现服务器代码
BlogController 中添加queryBlogDetail ⽅法:

@RequestMapping("/getBlogDetail")
public BlogInfo queryBlogDetail(Integer blogId) {
    return blogService.queryBlogDetail(blogId);
}

在BlogService 中添加queryBlogDetail⽅法:

public BlogInfo queryBlogDetail(Integer blogId) {
    return blogMapper.selectById(blogId);
}

运行程序,访问http://127.0.0.1:8080/blog/getBlogDetail?blogId=3,测试后端接口的返回结果:
image.png
实现客户端代码
修改 blog_detail.html

  • 根据当前页面 URL 中的 blogId 参数(使用 location.search 即可得到形如 ?blogId=3 的数据), 给服务器发送 GET /blog 请求。
  • 根据获取到的响应数据, 显示在页面上
  1. 修改html⻚⾯, 去掉原来写死的博客标题, ⽇期和正⽂部分:
<div class="content">
  <div class="title"></div>
  <div class="date"></div>
  <div class="detail"></div>
  <div class="operating">
      <button onclick="window.location.href='blog_update.html'">编辑</button>
      <button onclick="deleteBlog()">删除</button>
  </div>
</div>
  1. 完善 js 代码, 从服务器获取博客详情数据:
// 获取博客详情
$.ajax({
    type: "get",
    url: "/blog/getBlogDetail"+location.search,
    success: function(result) {
        if (result.code == 200 && result.data != null) {
            var blog = result.data;
            $(".right .content .title").text(blog.title);
            $(".right .content .date").text(blog.createTime);
            $(".right .content .detail").text(blog.content);
        }
    }
});

运行程序,访问http://127.0.0.1:8080/blog_detail.html?blogId=3,观察页面返回的数据:
image.png
未完,后见下篇文章!

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
本篇文章是 Spring Boot 实践之十三 9 Spring Boot综合项目实战——个人博客系统管理模块 的续篇,本次将介绍如何实现个人博客系统的拓展模块。 在实际开发中,我们常常需要对系统进行扩展,添加一些新的功能模块。为了不影响原有代码的结构和功能,我们可以将这些新功能模块独立成为一个子模块,然后通过配置文件等方式将其与原有系统进行整合。 本文将以一个个人博客系统为例,介绍如何实现博客的拓展模块,具体包括以下几个方面: 1. 拓展模块的设计和实现 2. 拓展模块的集成和配置 3. 拓展模块的使用示例 ## 1. 拓展模块的设计和实现 在本例中,我们将实现一个博客系统的拓展模块,该模块主要提供以下两个功能: 1. 统计博客文章的阅读量并显示 2. 在博客页面添加底部的版权声明 ### 1.1 统计博客文章的阅读量并显示 首先,我们需要在数据库中添加一个字段来存储博客文章的阅读量。在本例中,我们在 `blog` 表中添加 `read_count` 字段来存储阅读量。 ```sql ALTER TABLE `blog` ADD COLUMN `read_count` INT NOT NULL DEFAULT 0 COMMENT '阅读量' AFTER `update_time`; ``` 接下来,在博客文章页面中添加一个阅读量的显示。我们可以在博客文章详情页面的右侧添加一个阅读量的区域,显示该文章的阅读量。具体的实现方式为: 1. 在博客文章详情页面中添加一个阅读量的区域。 2. 在加载博客文章详情页面时,通过 AJAX 请求统计该文章的阅读量,并更新阅读量区域的显示。 具体的代码实现如下: 在博客文章详情页面中添加一个阅读量的区域: ```html <div class="blog-sidebar-item"> <div class="blog-sidebar-title">阅读量</div> <div class="blog-sidebar-content"> <span id="read-count">0</span> </div> </div> ``` 在加载博客文章详情页面时,通过 AJAX 请求统计该文章的阅读量,并更新阅读量区域的显示。具体的实现方式为: ```javascript $(function () { // 统计阅读量 var blogId = $("#blogId").val(); $.ajax({ url: "/blog/read/" + blogId, type: "POST", success: function (result) { if (result && result.success) { $("#read-count").text(result.data); } else { $("#read-count").text(0); } } }); }); ``` 在服务器端,我们需要实现一个接口来统计博客文章的阅读量,并将其保存到数据库中。具体的实现方式为: ```java @RestController @RequestMapping("/blog") public class BlogController { ... /** * 统计博客文章的阅读量 * * @param blogId 博客文章ID * @return 统计结果 */ @PostMapping("/read/{blogId}") public Result<Integer> readBlog(@PathVariable("blogId") Long blogId) { int readCount = blogService.readBlog(blogId); return Result.success(readCount); } ... } ``` 在 `BlogService` 中实现 `readBlog` 方法: ```java @Service public class BlogServiceImpl implements BlogService { ... /** * 统计博客文章的阅读量 * * @param blogId 博客文章ID * @return 统计结果 */ @Override public int readBlog(Long blogId) { Blog blog = blogMapper.selectByPrimaryKey(blogId); if (blog != null) { int readCount = blog.getReadCount() + 1; blog.setReadCount(readCount); blogMapper.updateByPrimaryKeySelective(blog); return readCount; } return 0; } ... } ``` ### 1.2 在博客页面添加底部的版权声明 接下来,我们将在博客页面底部添加一个版权声明。具体的实现方式为: 1. 在博客页面底部添加一个版权声明的区域。 2. 在加载博客页面时,通过 AJAX 请求获取版权声明的内容,并更新版权声明区域的显示。 具体的代码实现如下: 在博客页面底部添加一个版权声明的区域: ```html <div class="blog-footer"> <div><span id="copyright"> 版权声明:本博客所有文章均为作者原创或转载,未经授权禁止转载。 </span></div> </div> ``` 在加载博客页面时,通过 AJAX 请求获取版权声明的内容,并更新版权声明区域的显示。具体的实现方式为: ```javascript $(function () { // 加载版权声明 $.ajax({ url: "/blog/copyright", type: "GET", success: function (result) { if (result && result.success) { $("#copyright") .html("版权声明:" + result.data); } } }); }); ``` 在服务器端,我们需要实现一个接口来获取版权声明的内容。具体的实现方式为: ```java @RestController @RequestMapping("/blog") public class BlogController { ... /** * 获取版权声明的内容 * * @return 版权声明的内容 */ @GetMapping("/copyright") public Result<String> getCopyright() { String content = "本博客所有文章均为作者原创或转载,未经授权禁止转载。"; return Result.success(content); } ... } ``` ## 2. 拓展模块的集成和配置 在上一篇文章中,我们已经将博客系统的所有模块都整合到了一个工程中,因此我们可以通过添加一个 Maven 模块来实现拓展模块的开发,并将其整合到原有工程中。 具体的步骤如下: 1. 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`,并将其添加到工程中。 2. 在 `blog-ext` 模块中添加 `pom.xml` 文件,并添加依赖关系。 3. 在 `blog-ext` 模块中添加 Spring Boot 的配置文件 `application.yml`,并添加相关配置。 4. 在 `blog-ext` 模块中添加拓展模块的代码和资源文件。 ### 2.1 添加 Maven 模块 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`,并将其添加到工程中。具体的步骤如下: 1. 在项目根目录下创建一个新的 Maven 模块,命名为 `blog-ext`。 ```bash $ cd ~/workspace/springboot-blog $ mvn archetype:generate -DgroupId=com.waylau.spring.boot.blog \ -DartifactId=blog-ext -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false ``` 2. 将 `blog-ext` 模块添加到工程中。 ```xml <modules> <module>blog-api</module> <module>blog-service</module> <module>blog-web</module> <module>blog-ext</module> </modules> ``` ### 2.2 添加依赖关系 在 `blog-ext` 模块中添加 `pom.xml` 文件,并添加依赖关系。具体的依赖关系如下: ```xml <dependencies> <dependency> <groupId>com.waylau.spring.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 添加 Spring Web MVC 的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 添加 MyBatis 的依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.boot.version}</version> </dependency> </dependencies> ``` ### 2.3 添加配置文件 在 `blog-ext` 模块中添加 Spring Boot 的配置文件 `application.yml`,并添加相关配置。具体的配置如下: ```yaml spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/blog?useSSL=false&useUnicode=true&characterEncoding=utf8 username: root password: root mvc: view: prefix: /templates/ suffix: .html resources: static-locations: classpath:/static/ ``` ### 2.4 添加拓展模块的代码和资源文件 在 `blog-ext` 模块中添加拓展模块的代码和资源文件。具体的步骤如下: 1. 在 `blog-ext` 模块中添加 `com.waylau.spring.boot.blog.ext` 包,并在该包下添加 `BlogExtApplication` 类。 ```java @SpringBootApplication(scanBasePackages = "com.waylau.spring.boot.blog.ext") public class BlogExtApplication { public static void main(String[] args) { SpringApplication.run(BlogExtApplication.class, args); } } ``` 2. 在 `blog-ext` 模块中添加 `resources` 目录,并在该目录下添加 `templates` 和 `static` 目录。 3. 在 `templates` 目录中添加 `read-count.html` 和 `copyright.html`。 ```html <!-- read-count.html --> <div class="blog-sidebar-item"> <div class="blog-sidebar-title">阅读量</div> <div class="blog-sidebar-content"> <span id="read-count">0</span> </div> </div> ``` ```html <!-- copyright.html --> <div class="blog-footer"> <div><span id="copyright"> 版权声明:本博客所有文章均为作者原创或转载,未经授权禁止转载。 </span></div> </div> ``` 4. 在 `static` 目录中添加 `js` 目录,并在该目录下添加 `read-count.js` 和 `copyright.js`。 ```javascript // read-count.js $(function () { // 统计阅读量 var blogId = $("#blogId").val(); $.ajax({ url: "/blog/read/" + blogId, type: "POST", success: function (result) { if (result && result.success) { $("#read-count").text(result.data); } else { $("#read-count").text(0); } } }); }); ``` ```javascript // copyright.js $(function () { // 加载版权声明 $.ajax({ url: "/blog/copyright", type: "GET", success: function (result) { if (result && result.success) { $("#copyright") .html("版权声明:" + result.data); } } }); }); ``` ## 3. 拓展模块的使用示例 在完成了拓展模块的开发和配置之后,我们需要将其与原有系统进行整合。具体的步骤如下: 1. 在原有系统中添加对拓展模块的依赖关系。 在 `blog-web` 模块的 `pom.xml` 文件中添加对 `blog-ext` 模块的依赖关系: ```xml <dependencies> ... <!-- 添加 blog-ext 的依赖 --> <dependency> <groupId>com.waylau.spring.boot.blog</groupId> <artifactId>blog-ext</artifactId> <version>${project.version}</version> </dependency> </dependencies> ``` 2. 在原有系统中添加拓展模块的使用示例。 在博客文章详情页面中添加一个阅读量的区域: ```html <!-- 添加阅读量的区域 --> <div th:replace="blog-ext :: read-count"></div> ``` 在博客页面底部添加一个版权声明的区域: ```html <!-- 添加版权声明的区域 --> <div th:replace="blog-ext :: copyright"></div> ``` 经过以上的步骤,我们就已经成功地将博客系统的拓展模块整合到了原有系统中。 ## 总结 本文介绍了如何实现 Spring Boot 的拓展模块,并将其与原有系统进行整合。在实际开发中,我们可以根据具体的需求来实现不同的拓展模块,并通过配置文件等方式将其整合到原有系统中。这种方式既提高了代码的可维护性,又方便了模块的扩展和升级。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值