新手搭建Spring Boot项目

每次项目搭建,新项目都要重复配置各种依赖,看着那些复杂的框架配置就头疼,今天我要分享一个让无数程序员拍手叫好的方法。

用最简单的方式,搭建出一个企业级的后端项目框架。就算你是刚入门的小白,跟着这个教程,5分钟就能拥有一个完整可用的项目!

开始之前,先准备这些神器

想要顺利开车,得先有个好车对吧?我们的"车"就是这些基础环境:

必备装备清单:

  • JDK 17或更高版本(推荐21版本)
  • **MySQL数据库**\](8.x版本最香,5.7也能凑合用)

3步创建你的第一个项目

第1步:IDEA里的魔法操作

打开IDEA,就像打开一个魔法工具箱:

  1. 选择Spring Boot模板(这就是我们的"项目生成器")
  2. 选择Maven(想象成项目的"管家",帮你管理所有依赖)
  3. 选择JDK 21

项目创建示意图

第2步:选择你的"超能力"

Spring Boot就像一个超市,你可以选择需要的"超能力":

  • Spring Web:让你的项目能接收网络请求(必选!)
  • MySQL :连接数据库的桥梁(必选!)
  • Lombok:让代码变得超级简洁(强烈推荐!)
  • Spring Boot DevTools:开发时的贴心助手(可选)

依赖选择界面

点击创建,Maven就开始为你下载各种"超能力包"了。这时候可以去泡杯茶,等待这个过程完成。

第3步:第一次"试驾"

项目创建好后,最重要的事情就是------启动它!

项目启动示意图

看到控制台输出"Started"字样,恭喜你!你的项目已经成功运行了。这时候可以提交代码到Git,给自己一个小小的成就感。

小贴士: 如果Lombok报错,别慌!手动指定版本就好了:

xml 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.36</version>
    <optional>true</optional>
</dependency>

接下来,把配置文件从application.properties改成application.yml(YAML格式更直观),然后添加这些配置:

yaml 复制代码
spring:
  application:
    name: demo
server:
  port: 8123
  servlet:
    context-path: /api

这就相当于给你的项目起了个名字,指定了它的"门牌号"(端口8123)和"地址前缀"(/api)。

给项目装上"涡轮增压器"

基础项目有了,但还需要一些"高级配件"让它更强大!

神器1:Hutool工具库 - 你的贴心小助手

想象一下,如果有一个万能工具箱,里面有各种常用工具:日期处理、字符串操作、文件处理、加密解密...这就是Hutool!

比如格式化日期,原来要写一大堆代码,现在一行搞定:DateUtil.formatDate(new Date())

安装方法:

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.38</version>
</dependency>

神器2:Knife4j接口文档 - 自动生成说明书

你写的接口别人怎么知道怎么用?Knife4j就像一个自动说明书生成器,不仅能生成漂亮的文档,还能直接在页面上测试接口!

重要提醒: 我们用的是Spring Boot 3.x,一定要选OpenAPI 3版本!

安装步骤:

1)添加依赖:

xml 复制代码
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

2)在配置文件中告诉它扫描哪个包:

yaml 复制代码
# springdoc-openapi
springdoc:
  group-configs:
    - group: 'default'
      packages-to-scan: com.it666.demo.controller
# knife4j
knife4j:
  enable: true
  setting:
    language: zh_cn

3)写个测试接口:

less 复制代码
@Tag(name = "健康检查", description = "系统健康状态检查接口")
@RestController
@RequestMapping("/health")
public class HealthController {

    @Operation(summary = "健康检查", description = "检查系统是否正常运行")
    @GetMapping("/")
    public String healthCheck() {
        return "ok";
    }
}

重启项目,访问 http://localhost:8123/api/doc.html ,你会看到一个超级漂亮的接口文档页面!

接口文档界面

可选神器:AOP切面编程

如果你想要更高级的功能,比如记录日志、权限控制等,可以加上这个:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

你甚至可以在启动类上加个注解(但其实不加也行,Spring Boot已经默认开启了):

less 复制代码
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

企业级项目的"标准配置"

接下来是重头戏!这些代码在任何企业级项目中都会用到,可以说是"一次编写,终身受用"的那种。

项目结构长这样:

scss 复制代码
└── src
    └── main
        ├── java
        │   └── com
        │       └── it666
        │           └── demo
        │               ├── config (配置类)
        │               ├── controller (接口层)
        │               ├── exception (异常处理)
        │               ├── common (通用类)
        │               └── utils (工具类)
        └── resources
            └── application.yml

异常处理:让错误变得"优雅"

在企业级项目中,错误处理非常重要。我们要让错误变得"可预测"、"可控制"。

第1步:定义错误码

就像HTTP状态码一样,我们也要有自己的错误码体系:

arduino 复制代码
/**
 * 自定义错误码枚举
 * 
 * @author NEO
 */
@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;
    }
}

小技巧: 错误码最好和HTTP状态码保持一致,比如40100对应401未授权,这样更容易记住!

第2步:自定义业务异常

不要直接抛Java内置的异常,我们要有自己的"专属异常":

java 复制代码
/**
 * 自定义业务异常类
 * 
 * @author NEO
 */
@Getter
public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    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();
    }
}

第3步:简化抛异常的代码

写个工具类,让抛异常变得超级简单:

typescript 复制代码
/**
 * 抛异常工具类
 * 
 * @author NEO
 */
public class ThrowUtils {

    private 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));
    }
}

以后检查参数就超级简单了:

ini 复制代码
// 如果用户ID为空,就抛出参数错误异常
ThrowUtils.throwIf(userId == null, ErrorCode.PARAMS_ERROR, "用户ID不能为空");

响应格式:让前端开发者爱上你的接口

统一的响应格式就像统一的"包装盒",前端开发者一看就知道怎么处理:

kotlin 复制代码
/**
 * 通用响应类
 * 
 * @param <T> 数据类型
 * @author NEO
 */
@Data
public class BaseResponse<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 状态码
     */
    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());
    }
}

但每次都要手动创建ResponseBase对象太麻烦了,我们再写个工具类:

typescript 复制代码
/**
 * 返回结果工具类
 * 
 * @author NEO
 */
public class ResultUtils {

    private 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<Void> error(ErrorCode errorCode) {
        return new BaseResponse<>(errorCode);
    }

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

    /**
     * 失败响应(使用错误码枚举和自定义信息)
     *
     * @param errorCode 错误码
     * @param message   错误信息
     * @return 响应结果
     */
    public static BaseResponse<Void> error(ErrorCode errorCode, String message) {
        return new BaseResponse<>(errorCode.getCode(), null, message);
    }
}

现在返回数据就变得超级简单:

kotlin 复制代码
return ResultUtils.success(userList); // 成功
return ResultUtils.error(ErrorCode.PARAMS_ERROR); // 失败

全局异常处理:再也不怕程序崩溃

最怕的就是程序突然崩溃,用户看到一堆乱七八糟的错误信息。我们要做一个"安全网":

less 复制代码
/**
 * 全局异常处理器
 * 
 * @author NEO
 */
@Hidden
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理业务异常
     *
     * @param e 业务异常
     * @return 错误响应
     */
    @ExceptionHandler(BusinessException.class)
    public BaseResponse<Void> businessExceptionHandler(BusinessException e) {
        log.error("BusinessException: {}", e.getMessage(), e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理运行时异常
     *
     * @param e 运行时异常
     * @return 错误响应
     */
    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<Void> runtimeExceptionHandler(RuntimeException e) {
        log.error("RuntimeException: {}", e.getMessage(), e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
    }
}

重要提醒: 因为我们用的是Spring Boot 3.4+和OpenAPI 3的Knife4j,必须加上@Hidden注解,不然会有兼容性问题。

通用请求类:减少重复代码

有些请求参数在很多地方都会用到,比如分页、删除操作,我们可以封装成通用类:

分页请求:

ini 复制代码
/**
 * 分页请求
 * 
 * @author NEO
 */
@Data
@Schema(description = "分页请求参数")
public class PageRequest implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 当前页号
     */
    @Schema(description = "当前页号", example = "1")
    private int pageNum = 1;

    /**
     * 页面大小
     */
    @Schema(description = "页面大小", example = "10")
    private int pageSize = 10;

    /**
     * 排序字段
     */
    @Schema(description = "排序字段", example = "createTime")
    private String sortField;

    /**
     * 排序顺序(默认降序)
     */
    @Schema(description = "排序顺序", example = "descend")
    private String sortOrder = "descend";
}

删除请求:

kotlin 复制代码
/**
 * 删除请求
 * 
 * @author NEO
 */
@Data
@Schema(description = "删除请求参数")
public class DeleteRequest implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 要删除数据的ID
     */
    @Schema(description = "要删除的数据ID", required = true, example = "1")
    private Long id;
}

跨域配置:解决前后端分离的头疼问题

前端和后端分开部署时,经常会遇到跨域问题。简单说就是浏览器不让前端直接访问后端接口。

比如:

这就是跨域!

解决方法很简单,加个全局配置:

typescript 复制代码
/**
 * 全局跨域配置
 * 
 * @author NEO
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 对所有接口生效
        registry.addMapping("/**")
                // 允许发送Cookie
                .allowCredentials(true)
                // 允许所有域名访问(生产环境建议指定具体域名)
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

大功告成!来验证一下成果

最后,我们修改一下测试接口,让它使用我们的统一响应格式:

less 复制代码
/**
 * 健康检查控制器
 * 
 * @author NEO
 */
@Tag(name = "健康检查", description = "系统健康状态检查接口")
@RestController
@RequestMapping("/health")
public class HealthController {

    @Operation(summary = "健康检查", description = "检查系统是否正常运行")
    @GetMapping("/")
    public BaseResponse<String> healthCheck() {
        return ResultUtils.success("ok");
    }
}

重启项目,访问接口文档,你会发现响应格式变得非常规范!

写在最后

到这里,你已经拥有了一个企业级后端项目的完整框架!这个框架包含了:

  • ✅ 统一的异常处理
  • ✅ 规范的响应格式
  • ✅ 全局跨域配置
  • ✅ 自动生成的接口文档
  • ✅ 常用工具库集成

这套框架的最大优势是什么?

  1. 一次搭建,终身受用 - 这些代码可以在任何项目中复用
  2. 新手友好 - 即使是初学者也能快速上手
  3. 企业级标准 - 完全符合企业开发规范
  4. 高效开发 - 大大减少重复代码编写

现在你可以在这个基础上愉快地开发各种功能了!需要数据库操作?加上MyBatis-Plus。需要Redis缓存?加上Spring Data Redis。需要消息队列?加上RabbitMQ...

相关推荐
Moonbit3 小时前
MoonBit Pearls Vol.10:prettyprinter:使用函数组合解决结构化数据打印问题
前端·后端·程序员
数智顾问3 小时前
Flink ProcessFunction 与低层级 Join 实战手册:多流广告计费精确去重
java·spring boot·spring
欧雷殿3 小时前
典型程序员跨界做在地社区是怎样一种体验?
程序员·产品·创业
世界哪有真情4 小时前
Trae 蓝屏问题
前端·后端·trae
Moment4 小时前
NestJS 在 2025 年:对于后端开发者仍然值得吗 😕😕😕
前端·后端·github
Java水解4 小时前
【Spring】Spring事务和事务传播机制
后端·spring
文心快码BaiduComate4 小时前
文心快码实测Markdown排版工具开发
前端·后端·程序员
用户904706683574 小时前
java hutool 跟 JSONUtil 的关系
后端
渣哥4 小时前
原文来自于:[https://zha-ge.cn/java/128](https://zha-ge.cn/java/128)
javascript·后端·面试