大家好,我是小白。在Web应用开发中,掌握高效的后端框架组合至关重要。本文将带你使用SpringBoot 3和MyBatis-Plus 3构建一个生产级项目,不仅涵盖基础搭建,更深入探讨分层架构设计、异常处理、性能优化等实战技巧。文末附完整项目结构与代码,建议收藏学习!


一、 开发环境与工具链(专业版配置)

1.1 版本要求

  • JDK 17+(LTS版本,支持虚拟线程等新特性)
  • Maven 3.8+(推荐使用Maven Wrapper,避免环境差异)
  • MySQL 8.0+(支持窗口函数等高级特性)
  • IDEA 2023+(社区版即可,安装Lombok插件)

1.2 环境验证


bash

# 检查JDK版本(需输出17或更高)
java -version

# 检查Maven版本
mvn -v

# 配置Maven国内镜像(加速依赖下载)
# 在~/.m2/settings.xml中添加阿里云镜像
# 检查JDK版本(需输出17或更高) java -version  # 检查Maven版本 mvn -v  # 配置Maven国内镜像(加速依赖下载) # 在~/.m2/settings.xml中添加阿里云镜像 

图1:Maven镜像配置示意图(建议在IDEA中展示settings.xml配置)


二、 项目初始化与结构设计

2.1 使用Spring Initializr创建项目

关键配置:

  • Project: Maven
  • Language: Java
  • Spring Boot: 3.2.x(最新稳定版)
  • Group: com.example
  • Artifact: enterprise-webapp
  • Packaging: Jar
  • Java: 17

核心依赖:


<!-- Web场景 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis-Plus增强 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>

<!-- MySQL驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

<!-- Lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- 参数校验 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- 单元测试 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
 

xml

2.2 项目目录结构(DDD风格)


enterprise-webapp/
├── src/
│   ├── main/
│   │   ├── java/com/example/enterprise/
│   │   │   ├── config/           # 配置类
│   │   │   ├── controller/       # 控制层
│   │   │   ├── service/          # 业务层
│   │   │   │   ├── impl/         # 业务实现
│   │   │   ├── entity/           # 数据库实体
│   │   │   ├── dto/              # 数据传输对象
│   │   │   ├── vo/               # 视图对象
│   │   │   ├── mapper/           # 数据访问
│   │   │   ├── exception/        # 自定义异常
│   │   │   └── common/           # 公共工具
│   │   └── resources/
│   │       ├── application.yml   # 主配置
│   │       ├── mapper/           # XML映射文件
│   │       └── static/           # 静态资源
│   └── test/                     # 测试代码
└── pom.xml

text
enterprise-webapp/ ├── src/ │ ├── main/ │ │ ├── java/com/example/enterprise/ │ │ │ ├── config/ # 配置类 │ │ │ ├── controller/ # 控制层 │ │ │ ├── service/ # 业务层 │ │ │ │ ├── impl/ # 业务实现 │ │ │ ├── entity/ # 数据库实体 │ │ │ ├── dto/ # 数据传输对象 │ │ │ ├── vo/ # 视图对象 │ │ │ ├── mapper/ # 数据访问 │ │ │ ├── exception/ # 自定义异常 │ │ │ └── common/ # 公共工具 │ │ └── resources/ │ │ ├── application.yml # 主配置 │ │ ├── mapper/ # XML映射文件 │ │ └── static/ # 静态资源 │ └── test/ # 测试代码 └── pom.xml 

图2:IDEA中项目结构截图(展示清晰的包分层)


三、 深度整合MyBatis-Plus

3.1 生产级配置(application.yml)


server:
  port: 8080
  servlet:
    context-path: /api/v1  # API版本控制

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/enterprise_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: your_secure_password
    # 连接池配置(HikariCP)
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

mybatis-plus:
  configuration:
    # 日志输出(开发环境开启,生产环境关闭)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 驼峰命名自动转换
    map-underscore-to-camel-case: true
    # 全局懒加载
    lazy-loading-enabled: true
  type-aliases-package: com.example.enterprise.entity
  mapper-locations: classpath*:mapper/**/*.xml
  global-config:
    db-config:
      # 逻辑删除字段
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
 

yaml
server:  port: 8080  servlet:  context-path: /api/v1 # API版本控制  spring:  datasource:  driver-class-name: com.mysql.cj.jdbc.Driver  url: jdbc:mysql://localhost:3306/enterprise_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false  username: root  password: your_secure_password  # 连接池配置(HikariCP)  hikari:  maximum-pool-size: 20  minimum-idle: 5  connection-timeout: 30000  idle-timeout: 600000  max-lifetime: 1800000  mybatis-plus:  configuration:  # 日志输出(开发环境开启,生产环境关闭)  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 驼峰命名自动转换  map-underscore-to-camel-case: true  # 全局懒加载  lazy-loading-enabled: true  type-aliases-package: com.example.enterprise.entity  mapper-locations: classpath*:mapper/**/*.xml  global-config:  db-config:  # 逻辑删除字段  logic-delete-field: deleted  logic-delete-value: 1  logic-not-delete-value: 0 

3.2 代码生成器(解放双手)

MyBatis-Plus提供了强大的代码生成器,可以一键生成Entity、Mapper、Service、Controller。


// 在test目录下创建CodeGenerator.java
public class CodeGenerator {
    public static void main(String[] args) {
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        gc.setAuthor("小白");
        gc.setOpen(false);
        gc.setFileOverride(true);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/enterprise_db?useSSL=false");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("your_password");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.example.enterprise");
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude("user", "order"); // 指定要生成的表
        mpg.setStrategy(strategy);

        mpg.execute();
    }
}
 

java
// 在test目录下创建CodeGenerator.java public class CodeGenerator {  public static void main(String[] args) {  AutoGenerator mpg = new AutoGenerator();   // 全局配置  GlobalConfig gc = new GlobalConfig();  gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");  gc.setAuthor("小白");  gc.setOpen(false);  gc.setFileOverride(true);  mpg.setGlobalConfig(gc);   // 数据源配置  DataSourceConfig dsc = new DataSourceConfig();  dsc.setUrl("jdbc:mysql://localhost:3306/enterprise_db?useSSL=false");  dsc.setDriverName("com.mysql.cj.jdbc.Driver");  dsc.setUsername("root");  dsc.setPassword("your_password");  mpg.setDataSource(dsc);   // 包配置  PackageConfig pc = new PackageConfig();  pc.setParent("com.example.enterprise");  mpg.setPackageInfo(pc);   // 策略配置  StrategyConfig strategy = new StrategyConfig();  strategy.setNaming(NamingStrategy.underline_to_camel);  strategy.setColumnNaming(NamingStrategy.underline_to_camel);  strategy.setEntityLombokModel(true);  strategy.setRestControllerStyle(true);  strategy.setInclude("user", "order"); // 指定要生成的表  mpg.setStrategy(strategy);   mpg.execute();  } } 

图3:代码生成器执行过程截图


四、 生产级业务代码实战

4.1 数据库表设计


CREATE TABLE `user` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` VARCHAR(50) NOT NULL COMMENT '用户名',
  `email` VARCHAR(100) NOT NULL COMMENT '邮箱',
  `age` TINYINT UNSIGNED DEFAULT NULL COMMENT '年龄',
  `status` TINYINT DEFAULT 0 COMMENT '状态:0-正常,1-禁用',
  `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_email` (`email`),
  KEY `idx_created_time` (`created_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

sql
CREATE TABLE `user` (  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',  `username` VARCHAR(50) NOT NULL COMMENT '用户名',  `email` VARCHAR(100) NOT NULL COMMENT '邮箱',  `age` TINYINT UNSIGNED DEFAULT NULL COMMENT '年龄',  `status` TINYINT DEFAULT 0 COMMENT '状态:0-正常,1-禁用',  `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',  `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `updated_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_username` (`username`),  UNIQUE KEY `uk_email` (`email`),  KEY `idx_created_time` (`created_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; 

4.2 实体类(Entity)设计


package com.example.enterprise.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

@Data
@TableName("user") // 指定表名
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度在3-20之间")
    private String username;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    private Integer age;

    @TableLogic // 逻辑删除注解
    private Integer deleted;

    @TableField(fill = FieldFill.INSERT) // 插入时自动填充
    private LocalDateTime createdTime;

    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
    private LocalDateTime updatedTime;
}
 

java
package com.example.enterprise.entity;  import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.time.LocalDateTime;  @Data @TableName("user") // 指定表名 public class User {  @TableId(type = IdType.AUTO)  private Long id;   @NotBlank(message = "用户名不能为空")  @Size(min = 3, max = 20, message = "用户名长度在3-20之间")  private String username;   @NotBlank(message = "邮箱不能为空")  @Email(message = "邮箱格式不正确")  private String email;   private Integer age;   @TableLogic // 逻辑删除注解  private Integer deleted;   @TableField(fill = FieldFill.INSERT) // 插入时自动填充  private LocalDateTime createdTime;   @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充  private LocalDateTime updatedTime; } 

4.3 DTO与VO设计(分层精髓)


// DTO:数据传输对象(接收前端请求)
@Data
public class UserCreateDTO {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式错误")
    private String email;

    private Integer age;
}

// VO:视图对象(返回前端数据)
@Data
public class UserVO {
    private Long id;
    private String username;
    private String email;
    private Integer age;
    private String status;
    private String createdTime;
}
 

java
// DTO:数据传输对象(接收前端请求) @Data public class UserCreateDTO {  @NotBlank(message = "用户名不能为空")  private String username;   @NotBlank(message = "邮箱不能为空")  @Email(message = "邮箱格式错误")  private String email;   private Integer age; }  // VO:视图对象(返回前端数据) @Data public class UserVO {  private Long id;  private String username;  private String email;  private Integer age;  private String status;  private String createdTime; } 

4.4 自定义异常体系


// 基础异常
public class BusinessException extends RuntimeException {
    private final int code;
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }
}

// 用户相关异常
public class UserNotFoundException extends BusinessException {
    public UserNotFoundException(Long id) {
        super(404, "用户不存在: " + id);
    }

    public UserNotFoundException(String username) {
        super(404, "用户不存在: " + username);
    }
}
 

java
// 基础异常 public class BusinessException extends RuntimeException {  private final int code;  public BusinessException(int code, String message) {  super(message);  this.code = code;  } }  // 用户相关异常 public class UserNotFoundException extends BusinessException {  public UserNotFoundException(Long id) {  super(404, "用户不存在: " + id);  }   public UserNotFoundException(String username) {  super(404, "用户不存在: " + username);  } } 

4.5 Service层(业务核心)


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class) // 事务管理
    public UserVO createUser(UserCreateDTO dto) {
        // 1. 业务校验
        if (userMapper.selectCount(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, dto.getUsername())) > 0) {
            throw new BusinessException(400, "用户名已存在");
        }

        // 2. 数据转换
        User user = new User();
        BeanUtils.copyProperties(dto, user);

        // 3. 插入数据
        if (!save(user)) {
            throw new BusinessException(500, "创建用户失败");
        }

        // 4. 返回VO
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        vo.setStatus("正常");
        vo.setCreatedTime(user.getCreatedTime().toString());
        return vo;
    }

    @Override
    public UserVO getUserById(Long id) {
        User user = getById(id);
        if (user == null || user.getDeleted() == 1) {
            throw new UserNotFoundException(id);
        }

        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");
        return vo;
    }

    @Override
    public PageResult<UserVO> listUsers(PageQuery query) {
        // 使用MyBatis-Plus分页插件
        Page<User> page = new Page<>(query.getPage(), query.getSize());

        // 条件构造器
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getDeleted, 0)
              .orderByDesc(User::getCreatedTime);

        Page<User> userPage = userMapper.selectPage(page, wrapper);

        // 转换为VO列表
        List<UserVO> voList = userPage.getRecords().stream().map(user -> {
            UserVO vo = new UserVO();
            BeanUtils.copyProperties(user, vo);
            vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");
            return vo;
        }).collect(Collectors.toList());

        return new PageResult<>(userPage.getTotal(), voList);
    }
}
 

java
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {   @Autowired  private UserMapper userMapper;   @Override  @Transactional(rollbackFor = Exception.class) // 事务管理  public UserVO createUser(UserCreateDTO dto) {  // 1. 业务校验  if (userMapper.selectCount(new LambdaQueryWrapper<User>()  .eq(User::getUsername, dto.getUsername())) > 0) {  throw new BusinessException(400, "用户名已存在");  }   // 2. 数据转换  User user = new User();  BeanUtils.copyProperties(dto, user);   // 3. 插入数据  if (!save(user)) {  throw new BusinessException(500, "创建用户失败");  }   // 4. 返回VO  UserVO vo = new UserVO();  BeanUtils.copyProperties(user, vo);  vo.setStatus("正常");  vo.setCreatedTime(user.getCreatedTime().toString());  return vo;  }   @Override  public UserVO getUserById(Long id) {  User user = getById(id);  if (user == null || user.getDeleted() == 1) {  throw new UserNotFoundException(id);  }   UserVO vo = new UserVO();  BeanUtils.copyProperties(user, vo);  vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");  return vo;  }   @Override  public PageResult<UserVO> listUsers(PageQuery query) {  // 使用MyBatis-Plus分页插件  Page<User> page = new Page<>(query.getPage(), query.getSize());   // 条件构造器  LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();  wrapper.eq(User::getDeleted, 0)  .orderByDesc(User::getCreatedTime);   Page<User> userPage = userMapper.selectPage(page, wrapper);   // 转换为VO列表  List<UserVO> voList = userPage.getRecords().stream().map(user -> {  UserVO vo = new UserVO();  BeanUtils.copyProperties(user, vo);  vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");  return vo;  }).collect(Collectors.toList());   return new PageResult<>(userPage.getTotal(), voList);  } } 

4.6 Controller层(API入口)


@RestController
@RequestMapping("/users")
@Validated // 开启参数校验
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 创建用户
     */
    @PostMapping
    public ApiResult<UserVO> create(@Valid @RequestBody UserCreateDTO dto) {
        return ApiResult.success(userService.createUser(dto));
    }

    /**
     * 获取用户详情
     */
    @GetMapping("/{id}")
    public ApiResult<UserVO> get(@PathVariable Long id) {
        return ApiResult.success(userService.getUserById(id));
    }

    /**
     * 分页查询用户列表
     */
    @GetMapping
    public ApiResult<PageResult<UserVO>> list(
            @RequestParam(defaultValue = "1") Integer page,
            @RequestParam(defaultValue = "10") Integer size) {
        PageQuery query = new PageQuery(page, size);
        return ApiResult.success(userService.listUsers(query));
    }

    /**
     * 更新用户
     */
    @PutMapping("/{id}")
    public ApiResult<Void> update(@PathVariable Long id, @RequestBody UserCreateDTO dto) {
        // 实际业务中需要创建UpdateDTO
        userService.updateUser(id, dto);
        return ApiResult.success();
    }

    /**
     * 删除用户(逻辑删除)
     */
    @DeleteMapping("/{id}")
    public ApiResult<Void> delete(@PathVariable Long id) {
        userService.removeById(id);
        return ApiResult.success();
    }
}
 

java
@RestController @RequestMapping("/users") @Validated // 开启参数校验 public class UserController {   @Autowired  private UserService userService;   /**  * 创建用户  */  @PostMapping  public ApiResult<UserVO> create(@Valid @RequestBody UserCreateDTO dto) {  return ApiResult.success(userService.createUser(dto));  }   /**  * 获取用户详情  */  @GetMapping("/{id}")  public ApiResult<UserVO> get(@PathVariable Long id) {  return ApiResult.success(userService.getUserById(id));  }   /**  * 分页查询用户列表  */  @GetMapping  public ApiResult<PageResult<UserVO>> list(  @RequestParam(defaultValue = "1") Integer page,  @RequestParam(defaultValue = "10") Integer size) {  PageQuery query = new PageQuery(page, size);  return ApiResult.success(userService.listUsers(query));  }   /**  * 更新用户  */  @PutMapping("/{id}")  public ApiResult<Void> update(@PathVariable Long id, @RequestBody UserCreateDTO dto) {  // 实际业务中需要创建UpdateDTO  userService.updateUser(id, dto);  return ApiResult.success();  }   /**  * 删除用户(逻辑删除)  */  @DeleteMapping("/{id}")  public ApiResult<Void> delete(@PathVariable Long id) {  userService.removeById(id);  return ApiResult.success();  } } 

4.7 统一响应格式与全局异常处理


// 统一响应结果
@Data
public class ApiResult<T> {
    private int code = 200;
    private String message = "success";
    private T data;
    private long timestamp = System.currentTimeMillis();

    public static <T> ApiResult<T> success(T data) {
        ApiResult<T> result = new ApiResult<>();
        result.setData(data);
        return result;
    }

    public static <T> ApiResult<T> error(int code, String message) {
        ApiResult<T> result = new ApiResult<>();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public ApiResult<Void> handleBusinessException(BusinessException e) {
        return ApiResult.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult<Void> handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        return ApiResult.error(400, message);
    }

    @ExceptionHandler(Exception.class)
    public ApiResult<Void> handleException(Exception e) {
        log.error("系统异常: ", e);
        return ApiResult.error(500, "系统内部错误");
    }
}
 

java
// 统一响应结果 @Data public class ApiResult<T> {  private int code = 200;  private String message = "success";  private T data;  private long timestamp = System.currentTimeMillis();   public static <T> ApiResult<T> success(T data) {  ApiResult<T> result = new ApiResult<>();  result.setData(data);  return result;  }   public static <T> ApiResult<T> error(int code, String message) {  ApiResult<T> result = new ApiResult<>();  result.setCode(code);  result.setMessage(message);  return result;  } }  // 全局异常处理器 @RestControllerAdvice public class GlobalExceptionHandler {   @ExceptionHandler(BusinessException.class)  public ApiResult<Void> handleBusinessException(BusinessException e) {  return ApiResult.error(e.getCode(), e.getMessage());  }   @ExceptionHandler(MethodArgumentNotValidException.class)  public ApiResult<Void> handleValidationException(MethodArgumentNotValidException e) {  String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();  return ApiResult.error(400, message);  }   @ExceptionHandler(Exception.class)  public ApiResult<Void> handleException(Exception e) {  log.error("系统异常: ", e);  return ApiResult.error(500, "系统内部错误");  } } 

图4:Postman测试API截图(展示完整请求和响应)


五、 单元测试与质量保障

5.1 Mapper层测试


@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setUsername("testuser");
        user.setEmail("test@example.com");
        user.setAge(25);

        int result = userMapper.insert(user);
        assertEquals(1, result);
        assertNotNull(user.getId());
    }

    @Test
    void testSelectById() {
        User user = userMapper.selectById(1L);
        assertNotNull(user);
        assertEquals("testuser", user.getUsername());
    }
}
 

java
@SpringBootTest class UserMapperTest {   @Autowired  private UserMapper userMapper;   @Test  void testInsert() {  User user = new User();  user.setUsername("testuser");  user.setEmail("test@example.com");  user.setAge(25);   int result = userMapper.insert(user);  assertEquals(1, result);  assertNotNull(user.getId());  }   @Test  void testSelectById() {  User user = userMapper.selectById(1L);  assertNotNull(user);  assertEquals("testuser", user.getUsername());  } } 

5.2 Service层测试(Mock)


@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private UserServiceImpl userService;

    @Test
    void shouldThrowExceptionWhenUsernameExists() {
        // Given
        UserCreateDTO dto = new UserCreateDTO();
        dto.setUsername("existing");
        dto.setEmail("test@example.com");

        when(userMapper.selectCount(any())).thenReturn(1);

        // When & Then
        assertThrows(BusinessException.class, () -> userService.createUser(dto));
    }
}
 

java
@ExtendWith(MockitoExtension.class) class UserServiceTest {   @Mock  private UserMapper userMapper;   @InjectMocks  private UserServiceImpl userService;   @Test  void shouldThrowExceptionWhenUsernameExists() {  // Given  UserCreateDTO dto = new UserCreateDTO();  dto.setUsername("existing");  dto.setEmail("test@example.com");   when(userMapper.selectCount(any())).thenReturn(1);   // When & Then  assertThrows(BusinessException.class, () -> userService.createUser(dto));  } } 

六、 性能优化与最佳实践

6.1 数据库索引优化


-- 为常用查询字段添加索引
ALTER TABLE user ADD INDEX idx_username_status (username, status);
ALTER TABLE user ADD INDEX idx_created_time (created_time);

sql
-- 为常用查询字段添加索引 ALTER TABLE user ADD INDEX idx_username_status (username, status); ALTER TABLE user ADD INDEX idx_created_time (created_time); 

6.2 连接池调优

  • 开发环境:连接池大小 5-10
  • 生产环境:根据并发量调整,一般为 CPU核心数 * 2 + 1

6.3 MyBatis-Plus高级特性


// 条件构造器实战
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getUsername, "张")      // 模糊查询
      .eq(User::getStatus, 0)              // 精确查询
      .between(User::getAge, 18, 30)       // 范围查询
      .orderByDesc(User::getCreatedTime)   // 排序
      .last("LIMIT 10");                   // 追加SQL

// 分页插件配置(在配置类中)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}
 

java
// 条件构造器实战 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.like(User::getUsername, "张") // 模糊查询  .eq(User::getStatus, 0) // 精确查询  .between(User::getAge, 18, 30) // 范围查询  .orderByDesc(User::getCreatedTime) // 排序  .last("LIMIT 10"); // 追加SQL  // 分页插件配置(在配置类中) @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  return interceptor; } 

6.4 日志与监控


# 生产环境建议配置
logging:
  level:
    com.example.enterprise: DEBUG
    org.springframework.web: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

yaml
# 生产环境建议配置 logging:  level:  com.example.enterprise: DEBUG  org.springframework.web: INFO  pattern:  console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" 

七、 完整项目启动与验证

7.1 启动步骤

  1. 1.创建数据库 enterprise_db
  2. 2.执行表结构SQL
  3. 3.修改 application.yml 中的数据库配置
  4. 4.运行 EnterpriseWebappApplication.main()
  5. 5.访问 http://localhost:8080/api/v1/users

7.2 接口测试清单

  • ✅ POST /users - 创建用户(带参数校验)
  • ✅ GET /users/{id} - 获取用户详情(异常处理)
  • ✅ GET /users?page=1&size=10 - 分页查询
  • ✅ PUT /users/{id} - 更新用户
  • ✅ DELETE /users/{id} - 逻辑删除

图5:完整接口测试截图(展示所有接口的请求和响应)


八、 总结与进阶方向

通过本文,你已经掌握了:

1.✅ 生产级项目结构设计

2.✅ SpringBoot 3 + MyBatis-Plus 3深度整合

3.✅ 分层架构(Entity/DTO/VO)实践

4.✅ 异常处理与统一响应

5.✅ 单元测试与质量保障

6.✅ 性能优化与最佳实践

下一步学习方向:

  • 安全框架:集成Spring Security + JWT实现认证授权
  • 缓存:整合Redis实现数据缓存
  • 消息队列:集成RabbitMQ/Kafka处理异步任务
  • 分布式:Spring Cloud微服务架构
  • 部署:Docker容器化部署 + CI/CD流程
  • 作者:小白原创声明:本文为CSDN原创技术博文,转载请注明出处
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐