符合企业级开发规范的 Spring Boot + MyBatis-Plus 标准 MVC 结构完整示例

Java学生信息管理系统实战:Spring Boot+MyBatis-Plus从零搭建 在软件开发领域,CRUD(增删改查)操作是构建数据驱动型应用的基础,其原理是通过对数据库记录的创建、读取、更新和删除来实现业务逻辑。掌握高效的CRUD实现技术,对于提升开发效率和系统可维护性具有重要价值。Spring Boot作为Java领域主流的快速开发框架,通过自动配置和约定大于配置的理念,极大地简化了企业级应用的搭建过程。结合MyBatis-Plus这一强大的持久层增强工具,开发者可以基于MyBatis框架,以极简的代码实现单表和多表的复杂操作,显著提升开发效率。这种技术组合尤其适用于需要快速迭代的业 阅读详情

以下是一个符合企业级开发规范的 Spring Boot + MyBatis-Plus 标准 MVC 结构完整示例,涵盖:

  • ✅ 标准分层结构(Controller → Service → Mapper)
  • ✅ 统一请求参数封装(DTO)
  • ✅ 统一返回结果封装(Result)
  • ✅ 参数校验(@Valid + JSR-303)
  • ✅ 分页查询标准写法
  • ✅ 异常统一处理
  • ✅ 详细中文注释
  • ✅ 企业级命名规范与安全实践

✅ 企业级标准 MVC 示例 —— 用户管理模块


📁 项目结构(推荐)

src/main/java/com/example/demo
├── controller
│   └── UserController.java          ← 控制器层(本示例重点)
├── service
│   ├── IUserService.java            ← 服务接口
│   └── impl/UserServiceImpl.java    ← 服务实现
├── mapper
│   └── UserMapper.java              ← 数据访问层
├── entity
│   ├── User.java                    ← 实体类(数据库表映射)
│   └── dto
│       ├── UserCreateDTO.java       ← 创建用户请求DTO
│       ├── UserUpdateDTO.java       ← 更新用户请求DTO
│       └── UserQueryDTO.java        ← 查询条件DTO
├── vo
│   └── UserVO.java                  ← 返回给前端的视图对象(可选,本例简化用User)
├── config
│   └── WebConfig.java               ← Web配置(如统一异常处理)
└── util
    └── Result.java                  ← 统一返回结果封装

🧩 1. 统一返回结果类 —— Result.java

package com.example.demo.util;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

/**
 * 统一 API 返回结果封装类
 * <p>
 * 所有接口返回此结构,便于前端统一处理
 * code: 200=成功,其他=失败(可自定义业务码)
 * msg: 提示信息
 * data: 业务数据
 * </p>
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private int code;
    private String msg;
    private T data;

    // 成功返回(无数据)
    public static <T> Result<T> success() {
        return new Result<>(200, "操作成功", null);
    }

    // 成功返回(带数据)
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    // 失败返回
    public static <T> Result<T> error(String msg) {
        return new Result<>(500, msg, null);
    }

    // 自定义状态码失败
    public static <T> Result<T> error(int code, String msg) {
        return new Result<>(code, msg, null);
    }
}

📄 2. 实体类 —— User.java

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 用户实体类(对应数据库表 t_user)
 * 继承 BaseEntity 可统一管理公共字段(本例简化,直接写入)
 */
@Data
@TableName("t_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键ID(雪花算法)
     */
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;

    /**
     * 用户名(唯一,非空)
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 状态(0-禁用,1-启用)
     */
    private Integer status;

    /**
     * 创建时间(自动填充)
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 更新时间(自动填充)
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

🧾 3. 请求 DTO —— UserCreateDTO.java(创建用户)

package com.example.demo.entity.dto;

import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;

/**
 * 创建用户请求参数 DTO
 * <p>
 * 使用 JSR-303 注解进行参数校验
 * 前端传入 JSON 数据绑定至此对象
 * </p>
 */
@Data
public class UserCreateDTO {

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄不能小于0")
    private Integer age;

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

    @NotNull(message = "状态不能为空")
    private Integer status; // 0或1
}

🧾 4. 请求 DTO —— UserUpdateDTO.java(更新用户)

package com.example.demo.entity.dto;

import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Min;
import javax.validation.constraints.Email;

/**
 * 更新用户请求参数 DTO
 * <p>
 * id 为必填,其余字段可选(部分更新)
 * </p>
 */
@Data
public class UserUpdateDTO {

    @NotNull(message = "用户ID不能为空")
    private Long id;

    private String name;

    @Min(value = 0, message = "年龄不能小于0")
    private Integer age;

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

    private Integer status;
}

🧾 5. 请求 DTO —— UserQueryDTO.java(分页查询条件)

package com.example.demo.entity.dto;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

/**
 * 用户查询条件 DTO(用于分页搜索)
 */
@Data
public class UserQueryDTO {

    private String name; // 用户名模糊搜索

    private Integer minAge; // 最小年龄

    private Integer maxAge; // 最大年龄

    private Integer status; // 状态

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime startTime; // 创建时间起

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime endTime;   // 创建时间止
}

🖥️ 6. Controller 层 —— UserController.java(核心示例)

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.entity.dto.*;
import com.example.demo.service.IUserService;
import com.example.demo.util.Result;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

/**
 * 用户管理控制器(标准企业级写法)
 * <p>
 * 遵循 RESTful 风格:
 * - GET    /users          → 分页查询
 * - POST   /users          → 新增用户
 * - PUT    /users/{id}     → 更新用户(全量/部分)
 * - DELETE /users/{id}     → 删除用户(逻辑删除)
 * </p>
 * <p>
 * 参数校验:@Validated + @Valid
 * 统一返回:Result<T>
 * 分页标准:Page + IPage
 * </p>
 */
@RestController
@RequestMapping("/api/users")
@Validated // 开启方法参数校验
public class UserController {

    @Autowired
    private IUserService userService;

    /**
     * 分页查询用户列表(带条件搜索)
     *
     * @param current 当前页码(默认1)
     * @param size    每页大小(默认10)
     * @param query   查询条件(可选)
     * @return 分页结果
     */
    @GetMapping
    public Result<IPage<User>> listUsers(
            @RequestParam(defaultValue = "1") @Min(1) Integer current,
            @RequestParam(defaultValue = "10") @Min(1) Integer size,
            UserQueryDTO query) {

        Page<User> page = new Page<>(current, size);
        IPage<User> result = userService.searchUsers(query, page);
        return Result.success(result);
    }

    /**
     * 根据ID获取用户详情
     *
     * @param id 用户ID
     * @return 用户详情
     */
    @GetMapping("/{id}")
    public Result<User> getUserById(@PathVariable @NotNull(message = "ID不能为空") Long id) {
        User user = userService.getById(id);
        if (user == null) {
            return Result.error(404, "用户不存在");
        }
        return Result.success(user);
    }

    /**
     * 新增用户
     *
     * @param createDTO 创建用户参数(自动校验)
     * @return 操作结果
     */
    @PostMapping
    public Result<String> createUser(@Valid @RequestBody UserCreateDTO createDTO) {
        boolean saved = userService.createUser(createDTO);
        return saved ? Result.success("用户创建成功") : Result.error("创建失败");
    }

    /**
     * 更新用户信息(支持部分字段更新)
     *
     * @param updateDTO 更新参数(id必填,其余可选)
     * @return 操作结果
     */
    @PutMapping
    public Result<String> updateUser(@Valid @RequestBody UserUpdateDTO updateDTO) {
        boolean updated = userService.updateUser(updateDTO);
        return updated ? Result.success("用户更新成功") : Result.error("更新失败");
    }

    /**
     * 删除用户(逻辑删除)
     *
     * @param id 用户ID
     * @return 操作结果
     */
    @DeleteMapping("/{id}")
    public Result<String> deleteUser(@PathVariable @NotNull(message = "ID不能为空") Long id) {
        boolean deleted = userService.removeById(id);
        return deleted ? Result.success("用户删除成功") : Result.error("删除失败,用户可能不存在");
    }

    /**
     * 批量启用用户(演示批量操作)
     *
     * @param ids 用户ID列表
     * @return 操作结果
     */
    @PutMapping("/enable")
    public Result<String> enableUsers(@RequestBody @NotNull(message = "用户ID列表不能为空") Long[] ids) {
        boolean success = userService.enableUsers(java.util.Arrays.asList(ids));
        return success ? Result.success("启用成功") : Result.error("启用失败");
    }
}

⚙️ 7. Service 接口 —— IUserService.java

package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.User;
import com.example.demo.entity.dto.UserCreateDTO;
import com.example.demo.entity.dto.UserQueryDTO;
import com.example.demo.entity.dto.UserUpdateDTO;

/**
 * 用户服务接口
 */
public interface IUserService extends IService<User> {

    /**
     * 创建用户
     */
    boolean createUser(UserCreateDTO dto);

    /**
     * 更新用户
     */
    boolean updateUser(UserUpdateDTO dto);

    /**
     * 根据条件分页查询
     */
    IPage<User> searchUsers(UserQueryDTO query, Page<User> page);

    /**
     * 批量启用用户
     */
    boolean enableUsers(java.util.List<Long> ids);
}

⚙️ 8. Service 实现 —— UserServiceImpl.java

package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.entity.dto.UserCreateDTO;
import com.example.demo.entity.dto.UserQueryDTO;
import com.example.demo.entity.dto.UserUpdateDTO;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

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

    @Override
    public boolean createUser(UserCreateDTO dto) {
        User user = new User();
        user.setName(dto.getName());
        user.setAge(dto.getAge());
        user.setEmail(dto.getEmail());
        user.setStatus(dto.getStatus());
        // createTime/updateTime 由自动填充处理器处理
        return this.save(user);
    }

    @Override
    public boolean updateUser(UserUpdateDTO dto) {
        User user = new User();
        user.setId(dto.getId());
        user.setName(dto.getName());
        user.setAge(dto.getAge());
        user.setEmail(dto.getEmail());
        user.setStatus(dto.getStatus());
        // 只更新非空字段(MP 默认策略)
        return this.updateById(user);
    }

    @Override
    public IPage<User> searchUsers(UserQueryDTO query, Page<User> page) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

        // 动态拼接查询条件
        wrapper.like(StringUtils.hasText(query.getName()), User::getName, query.getName())
               .ge(query.getMinAge() != null, User::getAge, query.getMinAge())
               .le(query.getMaxAge() != null, User::getAge, query.getMaxAge())
               .eq(query.getStatus() != null, User::getStatus, query.getStatus())
               .ge(query.getStartTime() != null, User::getCreateTime, query.getStartTime())
               .le(query.getEndTime() != null, User::getCreateTime, query.getEndTime())
               .orderByDesc(User::getCreateTime);

        return this.page(page, wrapper);
    }

    @Override
    public boolean enableUsers(java.util.List<Long> ids) {
        if (ids == null || ids.isEmpty()) return true;
        return this.update(
            new LambdaUpdateWrapper<User>().set(User::getStatus, 1).in(User::getId, ids)
        );
    }
}

🔄 9. 统一异常处理(推荐配置)—— GlobalExceptionHandler.java

package com.example.demo.config;

import com.example.demo.util.Result;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

/**
 * 全局异常处理器
 * <p>
 * 捕获参数校验异常、业务异常等,统一返回 Result 格式
 * </p>
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理 @Validated + @RequestBody 校验失败
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> handleValidationException(MethodArgumentNotValidException e) {
        String errorMsg = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.error(400, errorMsg);
    }

    /**
     * 处理 @Validated + @RequestParam / @PathVariable 校验失败
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
        String errorMsg = e.getConstraintViolations().iterator().next().getMessage();
        return Result.error(400, errorMsg);
    }

    /**
     * 处理其他异常
     */
    @ExceptionHandler(Exception.class)
    public Result<String> handleException(Exception e) {
        e.printStackTrace();
        return Result.error(500, "系统内部错误,请联系管理员");
    }
}

📋 10. Mapper 接口 —— UserMapper.java

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<User> {
    // 可扩展自定义 SQL 方法
}

🌐 11. 启动类(确保扫描 Mapper)

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描 Mapper 接口
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

✅ 12. API 调用示例(Postman / Swagger)

➤ 查询用户分页(GET)

GET /api/users?current=1&size=10&name=张&status=1

➤ 创建用户(POST)

POST /api/users
{
  "name": "张三",
  "age": 25,
  "email": "zhangsan@example.com",
  "status": 1
}

➤ 更新用户(PUT)

PUT /api/users
{
  "id": 123456789012345678,
  "name": "张三丰",
  "age": 26
}

➤ 删除用户(DELETE)

DELETE /api/users/123456789012345678

➤ 批量启用(PUT)

PUT /api/users/enable
[123456789012345678, 123456789012345679]

✅ 13. 返回结果示例

➤ 成功响应

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "records": [...],
    "total": 100,
    "size": 10,
    "current": 1,
    "pages": 10
  }
}

➤ 失败响应(参数校验)

{
  "code": 400,
  "msg": "邮箱格式不正确",
  "data": null
}

➤ 失败响应(业务逻辑)

{
  "code": 404,
  "msg": "用户不存在",
  "data": null
}

✅ 14. 企业级规范总结

规范项说明
分层清晰Controller → Service → Mapper,职责分离
DTO 隔离请求参数用 DTO,不直接使用 Entity,避免暴露数据库字段或敏感信息
统一返回所有接口返回 Result<T>,前端统一处理
参数校验使用 @Valid + JSR-303 注解,避免脏数据进入 Service
分页标准使用 Page + IPage,前端传 current/size,后端返回完整分页信息
异常统一处理@RestControllerAdvice 捕获异常,返回友好提示
RESTful 风格使用标准 HTTP 方法(GET/POST/PUT/DELETE)
命名规范类名、方法名、变量名符合驼峰命名,接口路径小写中划线
安全实践不暴露数据库主键原始值(可用 ASSIGN_ID),敏感操作加权限校验

📌 推荐扩展:

  • ✅ 集成 Swagger 生成 API 文档
  • ✅ 集成 Spring Security 或 JWT 做权限控制
  • ✅ 添加操作日志注解 + AOP 切面记录
  • ✅ 使用 MapStruct 做 DTO ↔ Entity 转换(大型项目推荐)

通过以上完整示例,你可以在实际项目中构建标准化、可维护、易扩展、安全可靠的 Spring Boot + MyBatis-Plus 应用!

💡 提示:此结构已应用于多个中大型企业项目,可根据团队需求微调字段、校验规则、返回码等,核心架构保持稳定。

当然可以!以下是对您提到的 4 个推荐扩展 的详细优化与完善说明,包括:

✅ 每个扩展的作用
✅ 为什么推荐(解决什么痛点)
✅ 如何集成(代码示例 + 配置)
✅ 实际开发中的最佳实践建议


✅ 推荐扩展 1:集成 Swagger 生成 API 文档

📌 作用:

  • 自动生成 RESTful API 文档,支持在线调试、参数说明、响应结构预览。
  • 前端、测试、产品、后端团队协作统一入口。
  • 减少手写文档工作量,API 变更自动同步。

📌 为什么推荐?

  • ❌ 传统痛点:接口文档靠 Word/Excel/Yapi 手动维护,易过时、难同步。
  • ✅ Swagger 自动生成,与代码强绑定,实时更新。
  • ✅ 支持在线测试,减少 Postman 重复配置。
  • ✅ Spring Boot 3 推荐使用 SpringDoc OpenAPI(Swagger 3 替代品),兼容 Jakarta EE。

📌 如何集成(Spring Boot 3 + SpringDoc)

1. 添加依赖(pom.xml)

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.3.0</version> <!-- 请使用最新版 -->
</dependency>

2. 配置(application.yml)

springdoc:
  swagger-ui:
    path: /swagger-ui.html
    tags-sorter: alpha
    operations-sorter: alpha
  api-docs:
    path: /v3/api-docs
  default-produces-media-type: application/json
  default-consumes-media-type: application/json

3. Controller 添加注解(优化文档可读性)

@Tag(name = "用户管理", description = "用户增删改查相关接口")
@RestController
@RequestMapping("/api/users")
public class UserController {

    @Operation(summary = "分页查询用户", description = "支持按姓名、年龄、状态等条件筛选")
    @GetMapping
    public Result<IPage<User>> listUsers(
            @Parameter(description = "当前页码", example = "1") @RequestParam(defaultValue = "1") Integer current,
            @Parameter(description = "每页大小", example = "10") @RequestParam(defaultValue = "10") Integer size,
            UserQueryDTO query) {
        // ...
    }

    @Operation(summary = "创建用户")
    @PostMapping
    public Result<String> createUser(@Valid @RequestBody UserCreateDTO createDTO) {
        // ...
    }
}

4. 访问地址:

  • 文档 UI:http://localhost:8080/swagger-ui.html
  • OpenAPI JSON:http://localhost:8080/v3/api-docs

📌 最佳实践建议:

  • ✅ 为每个 Controller 和方法添加 @Tag 和 @Operation 注解。
  • ✅ 为每个参数添加 @Parameter 或 @Schema 说明。
  • ✅ 生产环境可通过配置关闭:springdoc.api-docs.enabled=false
  • ✅ 可集成 Knife4j 增强 UI(企业常用):knife4j-openapi3-ui

✅ 推荐扩展 2:集成 Spring Security + JWT 权限控制

📌 作用:

  • 对 API 接口进行身份认证(Authentication)和权限授权(Authorization)。
  • 支持基于角色、权限的细粒度访问控制。
  • 使用 JWT(无状态 Token)替代 Session,适合前后端分离和微服务架构。

📌 为什么推荐?

  • ❌ 传统痛点:裸奔接口、权限混乱、越权访问、Session 难扩展。
  • ✅ Spring Security 是 Java 生态最成熟的安全框架。
  • ✅ JWT 无状态、易扩展、适合分布式。
  • ✅ 可对接 OAuth2、LDAP、RBAC、ABAC 等复杂模型。

📌 如何集成(Spring Boot 3 + Spring Security + JWT)

1. 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>

2. JWT 工具类(JwtUtil.java)

@Component
public class JwtUtil {

    private final String SECRET_KEY = "yourSecretKeyShouldBeLongAndRandom"; // 应从配置读取
    private final long EXPIRATION = 86400000; // 24小时

    public String generateToken(String username, List<String> roles) {
        return Jwts.builder()
                .setSubject(username)
                .claim("roles", roles)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
}

3. Security 配置(SecurityConfig.java)

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/api/auth/login").permitAll()
                .requestMatchers("/swagger-ui.html", "/v3/api-docs/**").permitAll() // 放行 Swagger
                .requestMatchers(HttpMethod.GET, "/api/users/**").hasAnyRole("USER", "ADMIN")
                .requestMatchers(HttpMethod.POST, "/api/users").hasRole("ADMIN")
                .requestMatchers(HttpMethod.PUT, "/api/users/**").hasRole("ADMIN")
                .requestMatchers(HttpMethod.DELETE, "/api/users/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4. JWT 过滤器(JwtAuthenticationFilter.java)

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        final String header = request.getHeader("Authorization");
        String username = null;
        String jwt = null;

        if (header != null && header.startsWith("Bearer ")) {
            jwt = header.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (jwtUtil.validateToken(jwt, username)) {
                UsernamePasswordAuthenticationToken authToken =
                    new UsernamePasswordAuthenticationToken(username, null, getAuthorities(jwt));
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        chain.doFilter(request, response);
    }

    private List<SimpleGrantedAuthority> getAuthorities(String token) {
        List<String> roles = jwtUtil.extractClaim(token, claims -> claims.get("roles", List.class));
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toList());
    }
}

5. 登录接口示例(AuthController.java)

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public Result<String> login(@RequestBody LoginDTO loginDTO) {
        // 模拟校验(实际应查数据库)
        if ("admin".equals(loginDTO.getUsername()) && "123456".equals(loginDTO.getPassword())) {
            String token = jwtUtil.generateToken(loginDTO.getUsername(), Arrays.asList("ADMIN"));
            return Result.success(token);
        }
        return Result.error("用户名或密码错误");
    }
}

📌 最佳实践建议:

  • ✅ 敏感接口(如删除、修改)必须加权限控制。
  • ✅ 使用 @PreAuthorize("hasRole('ADMIN')") 方法级注解。
  • ✅ JWT 密钥从配置中心或环境变量读取,禁止硬编码。
  • ✅ 设置合理的 Token 过期时间,支持 Refresh Token 机制。
  • ✅ 记录登录日志、失败次数、IP 限制等安全策略。

✅ 推荐扩展 3:添加操作日志注解 + AOP 切面记录

📌 作用:

  • 自动记录用户操作行为(如“张三修改了用户李四的信息”)。
  • 用于审计追踪、故障排查、合规审查。
  • 解耦业务代码与日志逻辑。

📌 为什么推荐?

  • ❌ 传统痛点:日志散落在业务代码中,难以维护、格式不统一、遗漏关键操作。
  • ✅ AOP 切面统一处理,业务无侵入。
  • ✅ 注解驱动,灵活控制哪些方法需要记录日志。
  • ✅ 支持记录操作人、IP、参数、耗时、结果等。

📌 如何集成(自定义注解 + AOP)

1. 自定义注解(@Log.java)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    String value() default ""; // 操作描述
    String module() default "系统模块"; // 模块名
}

2. 日志实体类(SysLog.java)

@Data
@TableName("sys_log")
public class SysLog {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String username;     // 操作人
    private String operation;    // 操作描述
    private String method;       // 方法名
    private String params;       // 请求参数(JSON)
    private String ip;           // IP地址
    private Long time;           // 耗时(毫秒)
    private LocalDateTime createTime;
}

3. AOP 切面(LogAspect.java)

@Aspect
@Component
public class LogAspect {

    @Autowired
    private ISysLogService logService; // 日志服务

    @Pointcut("@annotation(com.example.demo.annotation.Log)")
    public void logPointCut() {}

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();

        // 执行方法
        Object result = point.proceed();

        // 保存日志
        saveLog(point, System.currentTimeMillis() - beginTime);

        return result;
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        Log logAnnotation = method.getAnnotation(Log.class);

        SysLog sysLog = new SysLog();
        sysLog.setOperation(logAnnotation.value());
        sysLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());

        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        sysLog.setParams(JSON.toJSONString(args));

        // 获取用户名(从 SecurityContext 或 ThreadLocal)
        String username = SecurityContextHolder.getContext().getAuthentication() != null ?
                SecurityContextHolder.getContext().getAuthentication().getName() : "匿名用户";
        sysLog.setUsername(username);

        // 获取 IP
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        sysLog.setIp(request.getRemoteAddr());

        sysLog.setTime(time);
        sysLog.setCreateTime(LocalDateTime.now());

        // 异步保存(避免阻塞主流程)
        CompletableFuture.runAsync(() -> logService.save(sysLog));
    }
}

4. 在 Controller 方法上使用

@Log("创建用户")
@PostMapping
public Result<String> createUser(@Valid @RequestBody UserCreateDTO createDTO) {
    // ...
}

@Log("删除用户")
@DeleteMapping("/{id}")
public Result<String> deleteUser(@PathVariable Long id) {
    // ...
}

📌 最佳实践建议:

  • ✅ 关键业务操作(增删改)必须加 @Log。
  • ✅ 日志表做分区或归档,避免单表过大。
  • ✅ 使用异步保存(如 @Async 或 CompletableFuture),避免影响接口性能。
  • ✅ 敏感参数(如密码)应在保存前脱敏。
  • ✅ 结合 ELK 或 Grafana 做日志可视化分析。

✅ 推荐扩展 4:使用 MapStruct 做 DTO ↔ Entity 转换

📌 作用:

  • 在 DTO(数据传输对象)和 Entity(持久化对象)之间高性能、类型安全地转换。
  • 避免手动 set/get 或使用反射(如 BeanUtils)带来的性能损耗和类型隐患。

📌 为什么推荐?

  • ❌ 传统痛点:
    • 手动 set/get → 代码冗长、易漏字段、难维护。
    • BeanUtils.copyProperties → 性能差、类型不安全、无法定制逻辑。
  • ✅ MapStruct 编译期生成转换代码 → 零反射、高性能、类型安全。
  • ✅ 支持复杂映射、默认值、表达式、嵌套对象等。
  • ✅ 大型项目必备,提升代码健壮性和可维护性。

📌 如何集成(MapStruct + Lombok)

1. 添加依赖

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.6.3</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.6.3</version>
    <scope>provided</scope>
</dependency>

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

2. 配置编译插件(让 Lombok 和 MapStruct 兼容)

<!-- 以下内容参考自 MapStruct 官方 https://github.com/mapstruct/mapstruct-examples/blob/main/mapstruct-lombok/pom.xml-->
<!-- 中文文档 https://www.mapstruct.plus/mapstruct/1-5-5-Final.html -->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>21</source>
                <target>21</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.6.3</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.38</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

3. 定义映射器(UserMapper.java —— 注意不是 MyBatis Mapper)

// 注意:添加 org.mapstruct.Mapper 注解
@Mapper // @Mapper 注解将接口标记为映射接口,并允许 MapStruct 处理器在编译期间启动。
public interface UserStructMapper {

    // 实际的映射方法,期望以源对象作为参数,并返回目标对象。其名称可以自由选择。
    UserStructMapper INSTANCE = Mappers.getMapper(UserStructMapper.class);

    // 对于源对象和目标对象中具有不同名称的属性,可以使用 @Mapping 注解来配置名称。
    // DTO → Entity
    @Mapping(target = "password", ignore = true)
    @Mapping(target = "createTime", ignore = true) // 忽略自动填充字段
    @Mapping(target = "updateTime", ignore = true)
    @Mapping(source = "numberOfSeats", target = "seatCount")
    User toEntity(UserCreateDTO dto);

    // Entity → VO(查询返回用)
    UserVO toVO(User entity);

    // 批量转换
    List<UserVO> toVOList(List<User> entities);
}

// MapStruct 官方示例 https://github.com/mapstruct/mapstruct-examples

4. 在 Service 中使用

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

    @Autowired
    private UserStructMapper userStructMapper;

    @Override
    public boolean createUser(UserCreateDTO dto) {
        // 自动转换
        User user = userStructMapper.INSTANCE.toEntity( dto );
        return this.save(user);
    }

    @Override
    public UserVO getUserById(Long id) {
        User user = this.getById(id);
        return user != null ? userStructMapper.INSTANCE.toVO(user) : null;
    }

    @Override
    public IPage<UserVO> searchUsers(UserQueryDTO query, Page<User> page) {
        IPage<User> userPage = this.page(page, buildWrapper(query));
        // 转换分页数据
        List<UserVO> voList = userStructMapper.INSTANCE.toVOList(userPage.getRecords());
        IPage<UserVO> voPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
        voPage.setRecords(voList);
        return voPage;
    }
}

5. 定义 UserVO(返回给前端的对象)

@Data
public class UserVO {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private String statusDesc; // 转换后的状态描述(如“启用”)
    private String createTime;  // 格式化后的时间
}

并扩展映射器:

@Mapper
public interface UserStructMapper {

    @Mapping(target = "statusDesc", expression = "java(getStatusDesc(entity.getStatus()))")
    @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    UserVO toVO(User entity);

    default String getStatusDesc(Integer status) {
        return status == 1 ? "启用" : "禁用";
    }
}

📌 最佳实践建议:

  • ✅ 所有对外接口的入参用 DTO,出参用 VO,Entity 仅用于持久层。
  • ✅ 不要让 Entity 流出 Service 层(避免暴露数据库字段或敏感信息)。
  • ✅ 复杂转换逻辑用 default 方法或 @AfterMapping。
  • ✅ 使用 @Mapper(componentModel = "spring") 让 Spring 管理实例。
  • ✅ IDEA 安装 MapStruct 插件,支持跳转和错误提示。

✅ 总结:四大扩展的价值矩阵

扩展项解决痛点提升方向适用阶段
Swagger文档不同步、难调试协作效率、开发体验项目初期必选
Spring Security + JWT权限裸奔、越权风险系统安全、合规性有用户系统必选
AOP 操作日志审计无记录、排查困难可追溯性、运维能力中大型项目推荐
MapStruct手动转换易错、性能差代码质量、性能、安全大型项目必备

📌 最终建议架构图:

前端 → [Swagger UI] → Controller (@Log + @PreAuthorize)
       ↓
      DTO ←→ [MapStruct] ←→ Entity
       ↓
    Service → [MyBatis-Plus] → Mapper → Database
       ↓
    [AOP 日志] → SysLog 表
       ↓
    [Security Context] ← JWT ← Auth Filter

通过以上四大扩展的集成,你的 Spring Boot 项目将具备:

✅ 专业级 API 文档
✅ 企业级安全防护
✅ 完善的操作审计
✅ 高性能类型安全转换

这不仅是“能用”的系统,更是“好用、安全、可维护、可扩展”的企业级生产系统!

💡 温馨提示:可根据项目规模和团队能力逐步引入,不必一步到位。先上 Swagger 和 Security,再逐步加入日志和 MapStruct。

Spring Boot+MyBatis-Plus全栈实战:汽车4S店管理系统开发指南 在Java Web开发领域,Spring Boot作为简化Spring应用开发的事实标准框架,极大地提升了开发效率。其核心原理在于通过自动配置和起步依赖,快速构建独立、生产级的应用。结合MyBatis-Plus这一强大的ORM框架,开发者可以高效地进行数据库操作,实现业务逻辑与数据访问的解耦。这种技术组合在构建企业级管理系统时展现出巨大价值,尤其适用于需要快速迭代、清晰分层的项目。典型的应用场景包括客户关系管理(CRM)、进销存系统等后台管理系统。本文以汽车4S店管理系统这一实战案例为切入点,深入解析了如何 阅读详情

相关推荐

IDEA 2020创建SpringBoot项目全流程:从环境配置到MyBatis-Plus集成

Spring Boot作为Java领域主流的微服务框架,其核心在于简化配置、快速构建独立的生产级应用。其原理基于约定大于配置和自动装配,通过Starter依赖和嵌入式Servlet容器,极大地提升了开发效率与部署便捷性。这一技术价值在于让开发者能专注于业务逻辑,而非繁琐的环境搭建。其典型应用场景包括构建RESTful API、微服务架构以及企业级后台管理系统。本文聚焦于在IntelliJ IDEA 2020这一经典IDE环境中,如何高效搭建一个功能完备的SpringBoot项目。我们将深入探讨Maven依赖

weixin_33984032的博客 656

MybatisPlus建表、生成代码个人规范Demo

我们可以看到代码里Record这个Bean的含义、各属性的含义均被加上了注释,这样可读性、易理解性强。第二步:使用MyBatisPlus的AutoGenerator进行代码生成。第三步:将生成出来的代码文件放入你的classpath,并查看生成的代码。pom.xml(其中poi等部分如果你不需要则可以删除,保留你需要的)springboot配置文件(resource目录下)第一步:根据ER图写数据表建表语句。

HumorChen的博客 2203

SpringBoot使用Mybatis Plus详解含示例代码(值得珍藏)

本文将详细介绍如何在Spring Boot项目中集成和使用MyBatis Plus。我们将从环境准备、依赖管理、配置文件、实体映射、CRUD操作等方面进行阐述,并附有完整的示例代码和注释。通过阅读本文,你将能够了解如何在Spring Boot项目中使用MyBatis Plus进行高效的数据操作。

a342874650的专栏 2123

MyBatis-Plus——标准数据层开发

帮助文档:简介 | MyBatis-Plus (baomidou.com) 一、基本介绍 1.基本介绍 简化开发,提高效率 MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发,提高效率而生。 MyBatis-Plus提供了通用的Mapper和service,在不编写任何SQL语句的情况下,快速实现对表单的操作。 我们操作的实体类是由数据库中的表确定的。 那第一步就要扫描实体类,再通过反射技术将实体类中的属性进行抽..

weixin_51351637的博客 1504

Java Web实战:Spring Boot+MyBatis-Plus构建小区物业管理系统

在软件开发领域,掌握一个完整的项目实战流程是提升工程能力的关键。从概念上讲,基于Java Web技术栈构建业务管理系统,是理解MVC设计模式、ORM对象关系映射和分层架构原理的经典实践。其技术价值在于,通过一个贴近真实业务场景的项目,开发者能系统性地串联需求分析、数据库设计、后端业务逻辑实现与前端交互的全流程,从而深化对‘系统’二字的理解。具体到应用场景,例如小区物业管理系统,就涉及业主信息、收费项目、服务工单等多个核心模块的CRUD操作与复杂业务逻辑处理。本文以**Spring Boot**和**MyBa

weixin_33802505的博客 385

Java社区维修平台实战:Spring Boot+MyBatis-Plus从跑通到部署

社区维修系统是典型的轻量级Java Web业务应用,其核心涉及Spring Boot工程结构、MyBatis-Plus自动建表与逻辑删除、JWT权限控制及前后端联调部署。理解MyBatis-Plus如何根据Java实体类生成SQL并管理表结构,是避免手动建库踩坑的关键;掌握Spring Boot的application.yml配置规范(含MySQL时区、字符集、context-path)和端口避让策略,则直接决定项目能否本地启动。该类系统广泛应用于物业数字化、高校课程设计与Java工程师能力验证场景,本文聚

weixin_34315485的博客 367

MyBatis Plus框架与POJO类型的开发规范

所有POJO类都必须:存在公有的、无参数的构造方法所有属性都是私有的所有属性都有对应的Setter & Getter存在基于所有属性的equals()和hashCode()方法,并且,如果2个对象的所有属性值都相同,则equals()对比结果为true,且hashCode()值相同,反之,如果2个对象有任何属性值不同,则equals()对比结果为false,且hashCode()值不同。

m0_65199337的博客 604

从零构建企业级办公系统:Spring Boot+Vue3全栈实战与架构设计

企业级应用开发的核心在于构建清晰、可扩展的架构,以支撑复杂的业务流程与权限管理。其原理通常围绕分层设计与模块化展开,通过前后端分离、统一接口规范等技术手段,实现系统的高内聚、低耦合。在技术价值层面,一套良好的架构不仅能提升开发效率与代码可维护性,更能为系统的稳定性和安全性奠定基础。这类技术广泛应用于需要精细化权限控制与流程管理的场景,例如企业内部管理系统、OA办公自动化平台等。本文以热门的Spring Boot和Vue 3技术栈为例,深入探讨如何设计并实现一个涵盖组织权限、工作流审批、实时通知等核心模块的准

weixin_33906657的博客 318

Mybatis-plus 开发规范教程

Mybatis-plus 开发教程 编写普通接口继承BaseMapper IService的用法和IPage的用法

qq_51939838的博客 1864

基于Spring Boot的社团管理系统:从设计到部署的实战指南

在软件开发领域,Java因其稳定性、跨平台性和成熟的生态系统,成为构建企业级应用的首选语言。Spring Boot作为Java生态中的核心框架,通过“约定大于配置”的理念,极大地简化了应用的初始搭建和开发过程。其内嵌Web服务器、自动配置和生产就绪特性,让开发者能快速构建出稳定、可独立部署的微服务或单体应用,显著提升了开发效率和系统可维护性。这种技术组合在需要严谨数据管理和业务流程的系统中,如各类管理系统,展现出巨大价值。本文聚焦于一个具体的应用场景——社团管理,详细拆解如何利用Spring Boot、Sp

cuankuangzhong6373的博客 867

基于Spring Boot的网上订餐系统毕设完整实践指南

每一个管理类系统的开发都离不开成熟的后端框架与数据库设计。Spring Boot作为Java生态中主流的快速开发框架,通过自动配置与起步依赖简化了项目搭建;而MySQL关系型数据库则为订单、用户、菜品等核心数据提供了稳定存储。两者结合,使得网上订餐系统这类经典毕业设计能够清晰串联起用户端、商家端与管理后台的完整业务链路。围绕购物车、订单状态流转、权限拦截等关键环节,开发者需要理解实体分层、事务回滚和并发扣库存等工程实践。通过合理的数据库表设计与状态机定义,系统不仅实现了从点餐到配送的全流程管理,更充分体现了

cmff98425的博客 439

Spring Boot化妆品销售系统:从零搭建完整电商项目实战

Spring Boot作为Java领域主流的Web应用开发框架,通过约定优于配置的理念,极大地简化了企业级应用的初始搭建与开发流程。其核心原理在于自动配置、起步依赖和嵌入式容器,能够快速整合MyBatis-Plus、Thymeleaf等常用技术栈,显著提升开发效率。在技术价值层面,Spring Boot为构建高内聚、松耦合的**可落地**应用提供了标准范式,尤其适合需要快速验证业务模型的场景。在电商、管理后台等**应用场景**中,基于Spring Boot的完整项目案例,如化妆品销售系统,为学习者提供了从环

weixin_34384557的博客 410

基于Spring Boot与Vue的合同管理系统重构:从技术选型到核心业务实现

在企业级应用开发中,Java Web技术栈与微服务架构是构建稳定、高效后台系统的关键技术基础。其核心原理在于通过分层架构与组件化设计,实现业务逻辑解耦与高内聚,从而提升系统的可维护性与扩展性。Spring Boot作为主流框架,以其自动配置和快速启动能力,显著降低了项目搭建与部署的复杂度,其技术价值体现在能帮助团队聚焦核心业务开发,而非环境配置。结合Vue.js等前端框架,可实现前后端分离的现代化开发模式,提升开发效率与用户体验。这类技术组合广泛应用于需要强数据一致性、复杂业务流程与高安全要求的企业管理系统

congjukun0600的博客 472

Spring Boot实战:民间救援队救助系统设计与开发全解析

在Java Web开发领域,Spring Boot作为主流的快速应用开发框架,通过自动配置和约定优于配置的原则,极大地简化了企业级应用的搭建过程。其核心原理在于通过内嵌Servlet容器和Starter依赖,实现了开箱即用的开发体验,显著提升了开发效率。这一技术价值在于让开发者能够更专注于业务逻辑而非环境配置,尤其适合构建如管理系统、API服务等各类Web应用。在众多应用场景中,毕业设计和课程设计是学生将理论知识转化为工程实践的关键环节,一个功能完整、结构清晰的项目案例至关重要。本文聚焦于一个典型的Spri

weixin_33892359的博客 397

R链式中介:如何一次性生成4个变量所有可能的链式中介结果?(含结果解读与粗略作图)#bruceR#ggplot2

一次性生成4个变量所能生成的24种链式中介并作图

weixin_64183017的博客 3236

SRAM故障排查实战:如何用Nano Prober定位6T存储单元的异常点

本文详细介绍了在SRAM故障排查中,如何运用Nano Prober(纳米探针)技术精准定位6T存储单元的内部异常。面对传统方法难以发现的电性参数漂移(如晶体管阈值电压偏移)或微小物理缺陷,Nano Prober能直接对单个6T单元内的晶体管进行I-V特性测量,结合失效模式分析,有效解决读写错误等疑难问题,是芯片失效分析的关键工具。

v5w6x的博客 664

基于Spring Boot的民间救援队救助系统:毕业设计完整实现指南

在软件开发领域,企业级应用开发的核心在于将技术栈与真实业务场景深度融合,实现从技术实现到业务建模的思维跨越。Spring Boot作为Java生态中广受欢迎的快速开发框架,通过简化配置和提供丰富的Starter依赖,极大地提升了后端服务的构建效率。其技术价值在于能够帮助开发者聚焦核心业务逻辑,快速搭建出稳定、可扩展的RESTful API服务。结合MyBatis-Plus等持久层框架,可以高效地进行数据库操作与数据建模。这类技术组合的典型应用场景包括各类需要**数字化、流程化管理**的业务系统,例如电商平台

weixin_33739523的博客 420

cmu15445(2024fall) project 2

project 2比p1难度要更大,代码量更大且涉及的知识更多,实现中出现的bug更多,好在是最后全部解决了。我最大的收获在于对于多线程编程的理解更深入了,实际发现解决了多个多线程相关的bug,学会了更多多线程下debug的技巧方法,也见识了更多多线程的bug情况。在之后可以更系统的学习一下多线程编程。同时学会实现B+树也是需要掌握的基础。

Jerome_Yi的博客 1554

Spring Boot宠物医院管理系统:从需求分析到部署实战

在Java Web开发领域,Spring Boot凭借其“约定大于配置”的理念,已成为构建企业级应用的首选框架。其核心原理在于通过自动装配和Starter依赖,极大简化了Spring应用的初始化和配置过程,显著提升了开发效率。这一技术价值在于,它让开发者能更专注于业务逻辑的实现,而非繁琐的环境搭建。在诸如医疗、电商、OA等各类管理系统的开发中,Spring Boot都能提供稳定高效的支撑。本文聚焦于一个典型的应用场景——宠物医院管理系统,详细阐述了如何利用Spring Boot整合MyBatis-Plus、

weixin_33674976的博客 498

四足机械臂避坑指南:从URDF配置到逆运动学控制的完整流程

本文为四足机械臂(Loco-Manipulation)系统开发提供了一份详实的实战避坑指南。文章深入剖析了从URDF模型配置、核心控制循环设计到逆运动学(IK)求解与奖励函数构建的完整流程,重点讲解了如何避免关节命名错误、惯性参数设置不当、IK求解不稳定等常见陷阱,并强调了利用collision_bbox等可视化工具进行高效调试的方法,旨在帮助工程师构建稳定、高效的四足机械臂控制系统。

weixin_42509507的博客 191

【IT小趣闻】免费的DeepSeek是如何赚钱的?

导语:目前很多的通用人工智能也就是所谓的AGI例如文心一言、豆包、通义千问和最近大火的DeepSeek都是免费给普通用户使用的,对开源商业模式不太熟悉的普通人可不太了解这免费背后的商业套路,下面我们就一起来揭秘。开源模式能降低用户信任成本、加速技术普及,而商业化服务则针对企业级需求。DeepSeek 可通过组合上述策略,在服务开发者社区的同时,满足企业客户的深度需求,实现可持续发展。你也看出来了,上边是DeepSeek生成的,讲的还是挺明白的。

SunMy的博客 1762

实战指南:无需从零配置,用快马AI生成企业级Spring Boot博客系统完整代码

这个博客系统采用了Spring Boot作为基础框架,数据访问层使用MyBatis-Plus简化CRUD操作,数据库选择了MySQL。特别值得一提的是,MyBatis-Plus的代码生成器可以自动创建基础CRUD代码,省去了大量重复劳动。作为一个经常需要快速验证想法的开发者,我发现从零开始配置环境、搭建框架实在太耗时了,特别是当项目需要整合多个技术栈时。总结一下,通过这个项目我深刻体会到:现代Java开发已经不再是繁琐的配置和重复编码,合理利用现有工具和平台,可以让我们更专注于业务逻辑的实现。

AgatePanther34的博客 20

禁止使用服务器 IP 访问 Apache 环境下的 Web 站点

通过在 Apache 的配置文件中添加适当的指令,我们可以轻松地禁止使用服务器 IP 地址访问我们的 Web 站点。在 Apache 环境下,有时我们希望禁止用户使用服务器的 IP 地址直接访问我们的 Web 站点,而只允许使用域名进行访问。现在,当用户尝试使用服务器的 IP 地址来访问你的网站时,他们将会被拒绝访问,只有通过域名才能正常访问。为了实现这个目标,我们可以通过 Apache 的配置文件进行相应的设置。表示禁止对根目录的访问权限,即禁止使用服务器的 IP 地址进行访问。

TpCode的博客 944

Spring Boot汽车4S店管理系统实战:从零搭建到毕业设计

在Java Web开发领域,单体架构应用是理解MVC设计模式和业务逻辑实现的基础。其核心原理是通过控制器、服务和数据访问层的分层协作,处理用户请求并操作数据库。这种架构的技术价值在于结构清晰、易于维护,能够快速构建功能完整的业务系统。在应用场景上,它广泛适用于企业内部管理系统、电商后台等需要快速开发和部署的项目。本文以汽车4S店管理系统为例,深入解析如何整合Spring Boot、MyBatis-Plus和Thymeleaf等主流技术栈,实现客户管理、车辆库存等核心模块。通过剖析从实体类到前端页面的完整代码

weixin_34357928的博客 419

用 SAP S/4HANA Configure Predefined Custom Fields 玩转预定义自定义字段:从概念到实战

SAP预定义自定义字段配置指南:安全扩展业务模型的云原生方案 摘要: 本文系统介绍SAP S/4HANA Cloud中预定义自定义字段的配置方法,对比普通自定义字段与预定义字段的区别,阐述其技术特点与业务价值。重点解析"Configure Predefined Custom Fields"应用的核心功能,包括字段元数据配置、多语言支持、UI集成等能力,并通过业务伙伴扩展案例演示完整实施流程。文章指出预定义字段方案在云环境下的独特优势:既满足业务扩展需求,又能保证系统可升级性,是SaaS环

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 79

Unity零基础到入门 ☀️| 轻松学会 Unity界面布局和简单实例——入门级!(^_−)☆

入门级Unity知识,一起加油呀!

努力前行,总会成为自己心中的那道光 1万+
上一篇: MyBatis-Plus 通用接口 Service 与 Mapper 全面实战指南
下一篇: 数据校验(Validation) 常用的数据校验注解及其作用
龙茶清欢
博客等级 码龄7年 3720粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值