为什么需要个人通用模板?

提高项目初始化效率

  • 避免每次新建项目时重复配置基础依赖
  • 统一技术栈版本,减少兼容性问题
  • 快速集成常用组件(如MyBatis Plus等)

统一项目规范

  • 预置代码风格配置
  • 统一的日志格式和收集方案
  • 标准化异常处理机制

积累最佳实践

  • 封装经过验证的工具类
  • 沉淀业务通用解决方案
  • 记录踩坑经验与解决方案

新建项目

现在使用IDEA新建项目十分简单,直接左上角新建项目即可

对于jdk版本,开发的要求当然是越稳定越好,所以我们可以选择11或者17

但是注意,官方的Server Url不支持选择Java8和Java11,所以我们可以将上方的uel设置为https://start.aliyun.com/

同样是因为稳定性需求,我们可以选择Springboot2.7.6版本,点击下一步,然后在此页面我们可以加入一些自己常用的依赖

Spring WebMySQLLombok自然是少不了,此时勾选即可

选择完后直接创建即可

添加依赖

MyBatis-Plus

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生

在使用 MyBatis-Plus 时,通常不需要再单独引入 MyBatis 的依赖,因为 MyBatis-Plus 本身已经内置了 MyBatis 的核心依赖(即 mybatismybatis-spring),它会自动传递依赖进来

具体可以看文档:https://baomidou.com/

在Maven中的pom.xml中添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.12</version>
</dependency>

添加完以来后我们需要配置一下,在resource文件夹下删除原来的配置文件,新建一个application.yml文件

# Spring框架配置
# 配置Spring应用的基本信息和数据源连接
spring:
  # 应用配置
  application:
    # 应用名称,用于服务注册与发现
    name: 你的项目名字
  # 数据源配置
  datasource:
    # MySQL数据库驱动类名
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL
    url: jdbc:mysql://localhost:3306/dmoye_picture
    # 数据库用户名
    username: root
    # 数据库密码
    password: xxxxx

# MyBatis-Plus配置
# 配置MyBatis-Plus的映射规则和全局设置
mybatis-plus:
  # MyBatis配置项
  configuration:
    #MyBatis配置
    # 是否开启下划线到驼峰命名的自动转换
    map-underscore-to-camel-case: false
    #仅在开发环境使用
    # SQL执行日志实现类,用于控制台输出SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # MyBatis-Plus全局配置
  global-config:
    # 数据库配置
    db-config:
      # 逻辑删除字段名
      logic-delete-field: isDelete
      # 逻辑已删除值
      logic-delete-value: 1
      # 逻辑未删除值
      logic-not-delete-value: 0

我们配置好后,可以启动测试,发现项目可以正常启动

Hutool工具库

Hutool是一个 Java 工具类库,它封装了 Java 开发中常用的功能,提供了简单易用的 API,能够显著减少代码量,提高开发效率。Hutool 的目标是成为 Java 界的 "Swiss Army Knife"(瑞士军刀),涵盖工具方法、IO、加密、HTTP、缓存、JSON、Excel 等众多实用模块

官方文档:https://doc.hutool.cn/pages/index

在Maven中的pom.xml中添加依赖

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.38</version>
</dependency>

Knife4j 接口文档

Knife4j是一个为Java MVC框架集成Swagger生成API文档的增强解决方案,它集成了Swagger2和OpenAPI3规范,提供了一系列强大的文档展示和调试功能,让我们能够快速的调试我们开发的接口

文档:https://doc.xiaominfo.com/docs/quick-start#spring-boot-2

在Maven中的pom.xml中添加依赖

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

我们先创建一个controller包,然后配置yml属性,如下:

# Knife4j接口文档配置
# 配置Knife4j Swagger增强工具的相关参数
knife4j:
  # 是否启用Knife4j
  enable: true
  # OpenAPI规范配置
  openapi:
    # API文档标题
    title: Knife4j官方文档
    # API版本号
    version: v1.0
    # API分组配置
    group:
      test1:
        # 分组名称
        group-name: default
        # API规则类型
        api-rule: package
        # API资源路径配置
        api-rule-resources:
          # 控制器包路径
          - com.domye.xxxx.controller #自己的controller路径

最后,访问Knife4j的文档地址:http://ip:port/doc.html即可查看文档

通用基础代码

自定义异常

我们需要自定义错误码,对错误进行处理,便于前端管理

我们新建一个exception包,用来存放异常处理相关的类

@Getter
public enum ErrorCode {

    SUCCESS(0, "ok"),
    PARAMS_ERROR(40000, "请求参数错误"),
    NOT_LOGIN_ERROR(40100, "未登录"),
    NO_AUTH_ERROR(40101, "无权限"),
    NOT_FOUND_ERROR(40400, "请求数据不存在"),
    FORBIDDEN_ERROR(40300, "禁止访问"),
    SYSTEM_ERROR(50000, "系统内部异常"),
    OPERATION_ERROR(50001, "操作失败");

    /**
     * 状态码
     */
    private final int code;

    /**
     * 信息
     */
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

}

接下来,我们需要自定义一个异常类,就像空指针异常类、IO流异常类一样。此处自定义的异常类属于异常类,所有肯定是要继承一个异常类的,此处需要继承RuntimeException

相比Exception来讲,RuntimeException是在程序运行时才会爆出异常,在编译时是不会出现异常的,这就表示,如果你throw了一个RuntimeException,不需要做额外操作;而throw一个Exception,程序会要求你try-catch,否则你根本启动不了程序,程序会提示(必须对其进行捕获或声明以便抛出)

@Getter
public class BusinessException extends RuntimeException {

    /*
     * 错误码
     */

    private final int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    public BusinessException(ErrorCode errorCode, String message) {
        super(message);
        this.code = errorCode.getCode();
    }
}

对于这个代码,我们使用了super(message),这是因为

  • message 是 Throwable 的标准属性:Java 的异常体系中,Throwable 类(所有异常的基类)本身就有一个 detailMessage 字段用于存储异常信息。通过调用 super(message),消息会自动存储在父类的这个标准字段中
  • code 是业务扩展属性:错误码是业务层面的概念,Java 标准异常类中没有这个字段,所以需要在子类中自定义。

而现在这样写,抛出异常时,需要

throw new BusinessException(ErrorCode.PARAMS_ERROR);

显然这样的写法不够优雅,也不够便捷,于是我们可以再进行封装一次

public class ThrowUtils {

    /**
     * 条件成立则抛异常
     *
     * @param condition        条件
     * @param runtimeException 异常
     */
    public static void throwIf(boolean condition, RuntimeException runtimeException) {
        if (condition) {
            throw runtimeException;
        }
    }

    /**
     * 条件成立则抛异常
     *
     * @param condition 条件
     * @param errorCode 错误码
     */
    public static void throwIf(boolean condition, ErrorCode errorCode) {
        throwIf(condition, new BusinessException(errorCode));
    }

    /**
     * 条件成立则抛异常
     *
     * @param condition 条件
     * @param errorCode 错误码
     * @param message   错误信息
     */
    public static void throwIf(boolean condition, ErrorCode errorCode, String message) {
        throwIf(condition, new BusinessException(errorCode, message));
    }
}

响应包装

一般情况下,每个后端接口都要返回调用码、数据、调用信息等,前端可以根据这些信息进行相应的处理。

我们可以封装统一的响应结果类,便于前端统一获取这些信息

我们新建一个common包,创建BaseResponse

@Data
public class BaseResponse<T> implements Serializable {

    private int code;

    private T data;

    private String message;

    public BaseResponse(int code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }

    public BaseResponse(int code, T data) {
        this(code, data, "");
    }

    public BaseResponse(ErrorCode errorCode) {
        this(errorCode.getCode(), null, errorCode.getMessage());
    }
}

但之后每次接口返回值时,都要手动 new 一个 BaseResponse 对象并传入参数,比较麻烦,我们可以新建一个工具类,提供成功调用和失败调用的方法,支持灵活地传参,简化调用

public class ResultUtils {

    /**
     * 成功
     * @param data 数据
     * @param <T>  数据类型
     * @return 响应
     */
    public static <T> BaseResponse<T> success(T data) {
        return new BaseResponse<>(0, data, "ok");
    }

    /**
     * 失败
     * @param errorCode 错误码
     * @return 响应
     */
    public static BaseResponse<?> error(ErrorCode errorCode) {
        return new BaseResponse<>(errorCode);
    }

    /**
     * 失败
     * @param code    错误码
     * @param message 错误信息
     * @return 响应
     */
    public static BaseResponse<?> error(int code, String message) {
        return new BaseResponse<>(code, null, message);
    }

    /**
     * 失败
     * @param errorCode 错误码
     * @return 响应
     */
    public static BaseResponse<?> error(ErrorCode errorCode, String message) {
        return new BaseResponse<>(errorCode.getCode(), null, message);
    }
}

这样,我们返回的时候,就可以直接调用 ResultUtils.success(xxx)

全局异常处理器

为了防止意料之外的异常,利用 AOP 切面全局对业务异常和 RuntimeException 进行捕获:

▼java复制代码@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public BaseResponse<?> businessExceptionHandler(BusinessException e) {
        log.error("BusinessException", e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
        log.error("RuntimeException", e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
    }
}

请求包装

对于 分页这类通用的请求,可以封装统一的请求包装类,用于接受前端传来的参数,之后相同参数的请求就不用专门再新建一个类了

分页请求包装类,接受页号、页面大小、排序字段、排序顺序参数:

@Data
public class PageRequest {

    /**
     * 当前页号
     */
    private int current = 1;

    /**
     * 页面大小
     */
    private int pageSize = 10;

    /**
     * 排序字段
     */
    private String sortField;

    /**
     * 排序顺序(默认降序)
     */
    private String sortOrder = "descend";
}

全局跨域配置

跨域是指浏览器访问的 URL(前端地址)和后端接口地址的域名(或端口号)不一致导致的,浏览器为了安全,默认禁止跨域请求访问。

为了开发调试方便,我们可以通过全局跨域配置,让整个项目所有的接口支持跨域,解决跨域报错。

新建 config 包,用于存放所有的配置相关代码。全局跨域配置代码如下:

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 覆盖所有请求
        registry.addMapping("/**")
                // 允许发送 Cookie
                .allowCredentials(true)
                // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

编写示例接口

移除 controller 包下的其他代码,让项目干净一些,然后编写一个纯净的 /health 接口用于健康检查:

@RestController
@RequestMapping("/")
public class MainController {

    /**
     * 健康检查
     */
    @GetMapping("/health")
    public BaseResponse<String> health() {
        return ResultUtils.success("ok");
    }
}

健康检查是指可以通过访问该接口,来快速验证后端服务是否正常运行,所以该接口的返回值非常简单。

最后修改:2025 年 08 月 25 日
如果觉得我的文章对你有用,请随意赞赏