添加 framework 平台基础设施模块

本小节中,我们继续完善后端项目骨架 —— 抽取一个 framework 平台基础设施模块。如下图所示:

为什么要抽取一个 framework 基础设施模块?

在微服务架构中,如果每个服务都像一个独立的“孤岛”,虽然实现了业务解耦,但会导致大量重复的底层代码架构标准的不统一

抽取一个 Framework(基础设施/底座)模块,本质上是为了解决“微服务治理的复杂度”问题。


1. 减少重复造轮子 (DRY原则)

在微服务开发中,很多功能是跨业务通用的。如果没有统一的基础设施模块,每个微服务都要手动配置:

  • 异常处理:统一的全局异常拦截与错误码定义。
  • 日志记录:统一的日志格式(如:链路追踪 TraceID、请求 ID)。
  • 安全校验:JWT 解析、权限拦截、敏感词过滤。
  • 工具类:对象拷贝(BeanUtils)、JSON 处理、日期工具等。

核心价值:让开发者专注于业务逻辑(Business Logic),而不是反复配置基础设施。


2. 统一技术标准与版本管理

当项目规模扩大时,最头疼的就是“版本冲突”。

  • 依赖控制:在 Framework 中统一管理 Spring Boot、MyBatis、Swagger 等第三方库的版本,避免 A 服务用 2.x,B 服务用 3.x 导致的兼容性灾难。

  • 规范落地:比如强制要求所有接口返回统一的 JSON 格式:

    JSON

    { "code": 200, "data": {}, "msg": "success" }
    

3. 提升运维与监控效率

微服务越多,治理难度越大。Framework 模块可以集成以下监控切面:

  • 链路追踪:自动集成 SkyWalking 或 Zipkin。
  • 指标采集:预装 Prometheus 指标,自动暴露健康检查端点。
  • 限流降级:预置 Sentinel 或 Resilience4j 的默认配置。

4. 降低维护成本

假设公司决定从 Redis 切换到另一个缓存中间件,或者需要对敏感信息进行脱敏处理:

  • 有 Framework:只需修改一次 Framework 中的 common-redis 启动器,所有微服务升级依赖即可。
  • 无 Framework:你需要手动修改几十个微服务的代码,出错概率呈几何倍数增加。

新建 framework 模块

在项目上右键 | New | Module... , 新建一个子模块:

image-20260202221149638

解释一下标注的部分:

  • ①:选择 Maven Archetype 来创建一个 Maven 子模块;
  • ②:项目名称填写 hanserwei-framework
  • ③:项目使用的 JDK 版本,本项目使用的是 JDK 25
  • ④:父模块选择 hannote
  • ⑤:选择 Internal
  • ⑥:选择 maven-archetype-quickstart
  • ⑦:填写 Group 组织名称,通常为公司域名倒写,如 com.hanserwei
  • ⑧:项目的唯一标识符;

点击 Create 按钮开始创建 framework 子模块, 成功创建后,查看父模块的 pom.xml 文件,内容如下,可以看到 <modules> 节点中,自动添加上了该模块:

image-20260202221359031

编辑 hanserwei-framework 模块中的 pom.xml , 内容如下:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 父项目配置 -->
    <parent>
        <!-- 组织ID -->
        <groupId>com.hanserwei</groupId>
        <!-- 父项目工件ID -->
        <artifactId>hannote</artifactId>
        <!-- 版本号,使用变量引用 -->
        <version>${revision}</version>
    </parent>

    <!-- 当前模块工件ID -->
    <artifactId>hanserwei-framework</artifactId>
    <!-- 打包方式为pom,表示这是一个父模块 -->
    <packaging>pom</packaging>

    <!-- 项目名称 -->
    <name>${project.artifactId}</name>
    <!-- 项目描述 -->
    <description>平台基础设施层:封装一些常用功能,供各个业务线拿来即用</description>

    <!-- 项目属性配置 -->
    <properties>
        <!-- 项目源码编码格式 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- 子模块列表 -->
    <modules>

    </modules>

</project>

  • 指定了父项目是谁;
  • 打包方式和父模块一样,都是 pom 形式, 因为后续要在 framework 基础设施层中,添加很多个子模(封装各种业务组件)。

项目结构如下图所示:

注意 : 如果自动生成了 /src 目录,需要删除掉。

image-20260202221656436

添加 common 通用子模块

hanserwei-framewrok 模块上,继续右键 | New | Module... , 为基础设施层添加第一个子模块 —— hanserwei-common , 此模块为平台通用模块,主要放置一些通用枚举、工具类等等:

image-20260202221918744

填写相关选项,如下图所示:

image-20260202221934878

解释一下标注的部分:

  • ①:选择 Maven Archetype 来创建一个 Maven 子模块;
  • ②:项目名称填写 hanserwei-common
  • ③:项目使用的 JDK 版本,本项目使用的是 JDK 25
  • ④:父模块选择 hanserwei-framework
  • ⑤:选择 Internal
  • ⑥:选择 maven-archetype-quickstart
  • ⑦:组织 ID : com.hanserwei.framework.common
  • ⑧:项目的唯一标识符;

点击 Create 按钮创建子模块。同样的,创建完成以后,查看 hanserwei-framework 模块的 pom.xml 文件,确认一下 <modules> 节点是否有自动添加该通用工具组件:

image-20260202222122351

接着,编辑 hanserwei-common 模块的 pom.xml , 内容如下:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 父项目配置 -->
    <parent>
        <!-- 组织ID -->
        <groupId>com.hanserwei</groupId>
        <!-- 父项目名称 -->
        <artifactId>hanserwei-framework</artifactId>
        <!-- 版本号,使用变量引用 -->
        <version>${revision}</version>
    </parent>

    <!-- 当前模块的组织ID -->
    <groupId>com.hanserwei.framework.common</groupId>
    <!-- 当前模块的项目ID -->
    <artifactId>hanserwei-common</artifactId>
    <!-- 打包方式:jar包 -->
    <packaging>jar</packaging>

    <!-- 项目名称,引用项目ID -->
    <name>${project.artifactId}</name>
    <!-- 项目描述 -->
    <description>平台通用模块,如一些通用枚举、工具类等等</description>

    <!-- 项目属性配置 -->
    <properties>
        <!-- 项目源码编码格式 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- 项目依赖配置 -->
    <dependencies>
        <!-- Lombok依赖:用于简化Java代码,自动生成getter/setter等方法 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

  • 注意,这里指定了父项目为 hanserwei-framework, 打包方式为 Jar 包,添加一些模块描述性文字;
  • 然后添加了 Lombok 依赖。

删掉图中所示的部分:

image-20260202222359544

添加 Response 工具类

com.hanserwei.framework.common 包下,添加一个封装好的 Response 响参工具类,以及相关异常类:

package com.hanserwei.framework.common.exception;

/**
 * @author hanser
 */
public interface BaseExceptionInterface {


    /**
     * 获取异常码
     *
     * @return 异常码
     */
    String getErrorCode();

    /**
     * 获取异常信息
     *
     * @return 异常信息
     */
    String getErrorMessage();
}

定义业务异常类:

TIP : BizBusiness 英文的缩写,代表业务的意思。

package com.hanserwei.framework.common.exception;

import lombok.Getter;
import lombok.Setter;

/**
 * @author hanser
 */
@Getter
@Setter
public class BizException extends RuntimeException {

    /**
     * 异常码
     */
    private String errorCode;
    /**
     * 错误信息
     */
    private String errorMessage;

    public BizException(BaseExceptionInterface baseExceptionInterface) {
        this.errorCode = baseExceptionInterface.getErrorCode();
        this.errorMessage = baseExceptionInterface.getErrorMessage();
    }
}

封装响参工具类:

package com.hanserwei.framework.common.response;

import com.hanserwei.framework.common.exception.BaseExceptionInterface;
import com.hanserwei.framework.common.exception.BizException;
import lombok.Data;

import java.io.Serializable;

/**
 * 统一响应结果封装类
 *
 * @param <T> 响应数据类型
 * @author hanser
 */
@Data
public class Response<T> implements Serializable {

    /**
     * 是否成功,默认为 true
     */
    private boolean success = true;

    /**
     * 响应消息
     */
    private String message;

    /**
     * 异常码
     */
    private String errorCode;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 成功响应(无数据)
     *
     * @param <T> 响应数据类型
     * @return 成功响应对象
     */
    public static <T> Response<T> success() {
        return new Response<>();
    }

    /**
     * 成功响应(带数据)
     *
     * @param data 响应数据
     * @param <T>  响应数据类型
     * @return 成功响应对象
     */
    public static <T> Response<T> success(T data) {
        Response<T> response = new Response<>();
        response.setData(data);
        return response;
    }

    /**
     * 失败响应(无错误信息)
     *
     * @param <T> 响应数据类型
     * @return 失败响应对象
     */
    public static <T> Response<T> fail() {
        Response<T> response = new Response<>();
        response.setSuccess(false);
        return response;
    }

    /**
     * 失败响应(带错误消息)
     *
     * @param errorMessage 错误消息
     * @param <T>          响应数据类型
     * @return 失败响应对象
     */
    public static <T> Response<T> fail(String errorMessage) {
        Response<T> response = new Response<>();
        response.setSuccess(false);
        response.setMessage(errorMessage);
        return response;
    }

    /**
     * 失败响应(带错误码和错误消息)
     *
     * @param errorCode    错误码
     * @param errorMessage 错误消息
     * @param <T>          响应数据类型
     * @return 失败响应对象
     */
    public static <T> Response<T> fail(String errorCode, String errorMessage) {
        Response<T> response = new Response<>();
        response.setSuccess(false);
        response.setErrorCode(errorCode);
        response.setMessage(errorMessage);
        return response;
    }

    /**
     * 失败响应(基于业务异常)
     *
     * @param bizException 业务异常对象
     * @param <T>          响应数据类型
     * @return 失败响应对象
     */
    public static <T> Response<T> fail(BizException bizException) {
        Response<T> response = new Response<>();
        response.setSuccess(false);
        response.setErrorCode(bizException.getErrorCode());
        response.setMessage(bizException.getErrorMessage());
        return response;
    }

    /**
     * 失败响应(基于异常接口)
     *
     * @param baseExceptionInterface 异常接口对象
     * @param <T>                    响应数据类型
     * @return 失败响应对象
     */
    public static <T> Response<T> fail(BaseExceptionInterface baseExceptionInterface) {
        Response<T> response = new Response<>();
        response.setSuccess(false);
        response.setErrorCode(baseExceptionInterface.getErrorCode());
        response.setMessage(baseExceptionInterface.getErrorMessage());
        return response;
    }

}

至此,我们已经在 hanserwei-common 通用模块中,成功添加了响参工具类。这个功能是后续各个服务中,都需要用到的。

父模块中管理 common 模块版本号

接下来,我们将在 hannote-auth 认证服务中,引入 hanserwei-common 模块,并测试一下是否能够正常使用 Response 响参工具类。首先,需要在最外层的 pom 文件中,声明一下 hanserwei-common 依赖以及其版本号,如下:

image-20260202223157126

然后,编辑 hannote-auth 认证服务的 pom.xml 文件,引入该依赖:

image-20260202223257905

添加一个测试接口

将认证服务中添加一个 controller 包,并创建一个 TestController 用于测试:

image-20260202223426096

添加一个 /test 接口,并使用 Response 响参工具类进行成功响应,代码如下:

package com.hanserwei.hannote.auth.controller;

import com.hanserwei.framework.common.response.Response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author hanser
 */
@RestController
public class TestController {

    @GetMapping("/test")
    public Response<String> test() {
        return Response.success("Hello, Hanserwei!");
    }
}

重启认证服务,打开浏览器,访问 localhost:8080/test 接口,可以看到响应参数都是 OK 的:

image-20260202223528871

结语

本小节中,我们继续完善了小憨书的工程骨架,添加了 framework 基础设施模块,接着,在该模块中添加了 hanserwei-common通用模块,后续如一些业务上通用的枚举、工具类等等,都可以放置此模块中。最后,我们在认证服务的 pom.xml 中,引入了 common 模块,并创建了一个测试接口,通过 Response 工具类,对前端成功返回了响应参数。