作为一名即将毕业的软件工程学生,我深知毕业设计是大学四年知识的一次综合检验。它不仅要实现功能,更要体现软件工程的思想,从需求分析到系统设计,再到编码实现和文档撰写,每一步都至关重要。然而,现实往往是时间紧、任务重,技术选型眼花缭乱,文档要求又极其严格,很容易陷入“功能堆砌”和“复制粘贴”的泥潭,最终产出一个架构混乱、难以维护的项目。

幸运的是,AI辅助开发工具的兴起为我们提供了新的解题思路。它们不再是简单的代码补全,而是能理解上下文、生成符合工程规范的代码片段的智能伙伴。本文将结合我的亲身实践,分享如何利用AI工具,高效、高质量地完成一个基于Spring Boot的毕业设计项目,并确保其符合软件工程规范。

图片

1. 毕业设计常见痛点与AI破局思路

在开始之前,我们先明确几个典型的“坑”,这也是AI工具最能发挥价值的地方:

  1. 需求模糊与架构随意:很多同学拿到题目后,直接开始写Controller和Service,缺乏清晰的分层和模块划分。AI工具可以帮助我们快速搭建符合MVC或DDD思想的骨架代码,确保项目结构从起点就是清晰的。
  2. 重复劳动与效率低下:实体类、基础的CRUD接口、简单的Service方法,这些代码模式固定,却占用了大量时间。AI代码生成可以秒级完成这些“体力活”,让我们能聚焦于复杂的业务逻辑和算法实现。
  3. 文档缺失与论文压力:毕业设计论文要求详细的系统设计说明、接口文档等。手动维护文档不仅耗时,还容易与代码不同步。结合Swagger等工具和AI的注释生成能力,可以自动化产出高质量的API文档,部分内容甚至可以直接用于论文。
  4. 代码质量与规范问题:命名不规范、异常处理缺失、性能考虑不周等问题比比皆是。成熟的AI助手在生成代码时,会倾向于遵循常见的编码规范和安全实践,为我们提供了一个不错的“基线”参考。

2. 主流AI编程助手选型浅析

目前市面上有几款主流的AI编程助手,各有特色。对于学生党来说,选择一款免费、易用且对Java/Spring Boot支持良好的工具是关键。

  1. GitHub Copilot:无疑是行业的标杆,代码生成和理解能力非常强,对Spring Boot生态的支持也很到位。它擅长根据注释和上下文生成整段代码。缺点是收费,但学生可以通过GitHub学生包免费申请。
  2. 通义灵码(阿里云):国内开发者的福音,完全免费。它对中文注释的理解更友好,并且针对国内常用的技术栈(如MyBatis-Plus、Dubbo)有不错的支持。在生成Spring Boot相关代码时表现稳定,是性价比极高的选择。
  3. Amazon CodeWhisperer:同样提供免费套餐,与AWS服务集成紧密。如果你在项目中使用了AWS的相关组件,它会是不错的助手。但在纯粹的Spring Boot应用开发中,其通用性可能略逊于前两者。

对于毕业设计场景,我个人的推荐顺序是:通义灵码(免费优先)> GitHub Copilot(如果可获得学生授权)> CodeWhisperer。本文后续的示例将基于一种通用的“提示词”模式,这些模式在不同工具间基本是相通的。

3. 核心实现:用AI贯彻软件工程原则

软件工程的核心是管理复杂度,提高可维护性。我们利用AI,不是为了生成一堆无法理解的“黑盒”代码,而是让它帮助我们快速实现那些符合良好设计原则的部分。

原则一:清晰的分层架构 我们可以直接向AI描述我们想要的分层结构。例如,在创建用户管理模块时,可以在service包下的UserService.java文件里,先写下这样的注释:

// 用户服务接口,提供用户的增删改查、登录验证等业务逻辑。
// 需要实现以下方法:
// 1. 用户注册,密码需使用BCrypt加密存储。
// 2. 用户登录,验证用户名和密码,成功则返回JWT令牌。
// 3. 根据ID查询用户信息(返回DTO,隐藏密码字段)。
// 4. 分页查询用户列表。

然后触发AI补全(通常是按TabEnter),AI往往会生成一个结构清晰、方法签名完整的接口。接着,我们可以在impl包下的实现类中继续利用AI生成方法体骨架。

原则二:RESTful API设计 在Controller层,我们可以用注释明确约束。在UserController.java中写下:

// RESTful 风格的用户控制器
// 路径前缀:/api/users
// 需要包含以下端点:
// GET /{id} - 获取指定用户详情
// GET / - 分页查询用户列表,参数:page, size, username(可选)
// POST / - 创建新用户
// PUT /{id} - 更新用户信息
// DELETE /{id} - 删除用户
// POST /login - 用户登录

AI工具通常会生成使用@RestController@RequestMapping注解,并包含@GetMapping@PostMapping等对应映射的方法框架,我们只需要稍作调整和填充业务逻辑即可。

原则三:统一的异常处理 这是毕业设计中体现技术深度的好地方。我们可以让AI帮忙搭建一个全局异常处理框架。创建一个GlobalExceptionHandler.java

// 全局异常处理器,使用@ControllerAdvice。
// 需要处理以下异常:
// 1. 自定义业务异常(BusinessException),返回统一格式的错误消息和400状态码。
// 2. 数据未找到异常(如EntityNotFoundException),返回404状态码。
// 3. 参数校验异常(MethodArgumentNotValidException),返回400状态码及详细校验错误。
// 4. 所有其他未捕获异常,返回500状态码及通用错误信息。
// 统一响应格式为:{ code: number, message: string, data: any }

AI能够快速生成对应的@ExceptionHandler方法,大大减少了样板代码的编写。

4. 可运行的Spring Boot示例代码

下面是一个整合了上述思想的“用户注册与查询”模块的浓缩示例。请注意,以下代码块在AI辅助下生成,并经过人工调整和注释,确保其可运行性和可读性。

1. 实体类 (User.java)

package com.example.graduation.entity;

import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "sys_user")
@Data // Lombok注解,自动生成getter/setter等方法
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false, length = 50)
    private String username; // 用户名

    @Column(nullable = false)
    private String password; // 加密后的密码

    @Column(unique = true, length = 100)
    private String email; // 邮箱

    private String nickname; // 昵称

    @Column(name = "create_time", updatable = false)
    private LocalDateTime createTime; // 创建时间

    @PrePersist
    protected void onCreate() {
        createTime = LocalDateTime.now(); // 实体持久化前自动设置创建时间
    }
}

2. 数据传输对象 (UserDTO.java & UserRegisterDTO.java) 用于接口数据传输,与实体分离,避免暴露敏感信息或内部字段。

package com.example.graduation.dto;

import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Data
public class UserRegisterDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 50, message = "用户名长度需在3-50字符之间")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少6位")
    private String password;

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

    private String nickname;
}

@Data
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private String nickname;
    private LocalDateTime createTime;
}

3. Service层接口与实现 (UserService.java)

package com.example.graduation.service;

import com.example.graduation.dto.UserDTO;
import com.example.graduation.dto.UserRegisterDTO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface UserService {
    UserDTO register(UserRegisterDTO userRegisterDTO);
    UserDTO getUserById(Long id);
    Page<UserDTO> getUsersByPage(Pageable pageable, String username);
}
package com.example.graduation.service.impl;

import com.example.graduation.dto.UserDTO;
import com.example.graduation.dto.UserRegisterDTO;
import com.example.graduation.entity.User;
import com.example.graduation.repository.UserRepository;
import com.example.graduation.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;

@Service
@RequiredArgsConstructor // Lombok注解,为final字段生成构造函数
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    @Override
    @Transactional
    public UserDTO register(UserRegisterDTO dto) {
        // 检查用户名是否已存在
        if (userRepository.existsByUsername(dto.getUsername())) {
            throw new IllegalArgumentException("用户名已存在");
        }

        // DTO 转 Entity,并加密密码
        User user = new User();
        BeanUtils.copyProperties(dto, user, "password"); // 忽略密码字段的拷贝
        user.setPassword(passwordEncoder.encode(dto.getPassword()));

        User savedUser = userRepository.save(user);

        // Entity 转 DTO
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(savedUser, userDTO);
        return userDTO;
    }

    @Override
    public UserDTO getUserById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("用户不存在"));
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(user, dto);
        return dto;
    }

    @Override
    public Page<UserDTO> getUsersByPage(Pageable pageable, String username) {
        Page<User> userPage;
        if (username != null && !username.trim().isEmpty()) {
            userPage = userRepository.findByUsernameContaining(username, pageable);
        } else {
            userPage = userRepository.findAll(pageable);
        }
        // 使用 map 进行分页数据转换,避免循环查询
        return userPage.map(user -> {
            UserDTO dto = new UserDTO();
            BeanUtils.copyProperties(user, dto);
            return dto;
        });
    }
}

4. Controller层 (UserController.java)

package com.example.graduation.controller;

import com.example.graduation.dto.UserDTO;
import com.example.graduation.dto.UserRegisterDTO;
import com.example.graduation.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Api(tags = "用户管理") // Swagger API分组标签
public class UserController {

    private final UserService userService;

    @PostMapping("/register")
    @ResponseStatus(HttpStatus.CREATED)
    @ApiOperation("用户注册")
    public UserDTO register(@Valid @RequestBody UserRegisterDTO userRegisterDTO) {
        return userService.register(userRegisterDTO);
    }

    @GetMapping("/{id}")
    @ApiOperation("根据ID查询用户")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @GetMapping
    @ApiOperation("分页查询用户列表")
    public Page<UserDTO> getUsers(
            @PageableDefault(size = 10, sort = "id") Pageable pageable,
            @RequestParam(required = false) String username) {
        return userService.getUsersByPage(pageable, username);
    }
}

5. Swagger配置 (SwaggerConfig.java)

package com.example.graduation.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.graduation.controller")) // 扫描的Controller包路径
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("毕业设计项目 API 文档")
                .description("基于Spring Boot的用户管理系统接口说明")
                .version("1.0")
                .build();
    }
}

启动项目后,访问 http://localhost:8080/swagger-ui.html 即可看到自动生成的交互式API文档。

图片

5. AI生成代码的安全与性能考量

AI生成的代码是“建议”,而非“圣旨”。我们必须以审慎的态度进行审查。

  1. 安全性审查

    • SQL注入:如果AI生成了拼接字符串的SQL(在使用JdbcTemplate或原生SQL时可能出现),必须坚决改为使用参数化查询(PreparedStatement)或JPA的查询方法。确保Repository层使用的是@Query注解且参数绑定正确,或方法名衍生查询。
    • 敏感信息硬编码:检查AI是否将数据库密码、API密钥等直接写在代码里。必须将这些信息移至application.yml或环境变量中,通过@Value@ConfigurationProperties注入。
    • 输入验证:即使DTO中使用了@Valid注解,也要检查Service层是否对业务规则(如用户名唯一性)进行了二次校验。AI可能会遗漏这些业务逻辑。
    • 权限控制:对于涉及权限的接口,AI通常不会自动添加@PreAuthorize等注解,需要手动根据业务需求补充。
  2. 性能考量

    • N+1查询问题:在实体关联查询时(如User有多个Order),AI生成的代码可能会在循环中懒加载关联数据,导致大量SQL查询。需要手动优化,使用@EntityGraph或JPQL的FETCH JOIN一次性加载。
    • DTO转换效率:大量数据转换时,BeanUtils.copyProperties可能成为性能瓶颈。可以考虑使用MapStruct这类编译时生成代码的映射工具,AI也可以帮助我们生成MapStruct的Mapper接口。
    • 分页查询:务必确保查询方法使用了Spring Data JPA的Pageable参数,并且AI生成的Repository查询是支持分页的。避免一次性查询全表数据到内存中再分页。

6. 生产级避坑指南

为了让你的毕业设计更接近“生产级”项目,并顺利通过答辩,这里有几个关键建议:

  1. 严格的版本控制:从项目第一天就使用Git。为main分支设置保护规则。为每个新功能或修复创建特性分支(feature/*),开发完成后通过Pull Request合并。AI生成的代码也应该在特性分支上提交,经过审查后再合并。良好的Commit信息(如feat: 添加用户注册功能)能让历史清晰可读。
  2. AI生成内容的审核要点:建立自己的代码审查清单。每次接受AI的大段建议后,问自己几个问题:这段代码符合我的项目架构吗?变量名是否表意清晰?异常处理是否完备?有没有潜在的安全漏洞(如上述SQL注入)?性能上是否有优化空间?只有通过人工审核的代码才能入库。
  3. 论文查重规避建议:这是毕业设计的红线。AI生成的代码和文档(如Swagger描述、注释)也可能存在重复风险。
    • 代码部分:避免直接复制粘贴AI生成的、未经任何修改的通用性代码(例如一个标准的Spring Boot配置类)。应对其进行个性化修改,比如调整包结构、类名、方法名、添加自己独特的业务逻辑注释。
    • 文档/论文部分:绝对不要将AI生成的系统设计描述、功能介绍直接复制到论文中。AI生成的内容应作为参考和灵感。你需要用自己的语言重新组织、阐述,并结合自己项目的具体实现细节、遇到的真实问题和解决方案来写。阐述清楚“我为什么这么设计”比“这个系统有什么”更重要。
    • 核心算法与逻辑:这是体现你个人工作的关键部分,必须自己动手实现。AI可以辅助你完成周边的支撑代码,但核心算法、创新的业务逻辑必须由你亲自完成并深入理解。

结语

通过这次利用AI辅助完成Spring Boot毕业设计的实践,我最大的体会是:AI是一个强大的“加速器”和“启发者”,但它不是“替代者”。它极大地解放了我们在重复性、模式化编码上的生产力,让我们有更多时间去思考架构的合理性、业务逻辑的严密性以及系统的整体设计。

我建议你不妨现在就打开IDE,安装一款AI编程助手,从优化你现有项目的一个小模块开始。尝试用清晰的注释描述你的需求,看看AI能给你带来什么惊喜,同时也严格审视它生成的每一行代码。在这个过程中,你会更深刻地理解软件工程规范的价值,并开始思考那个终极问题:在未来,我们工程师与AI工具的协同边界究竟在哪里?我们的核心价值又将如何体现?这或许比完成毕业设计本身更有意义。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐