ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Spring Boot的银行客户管理系统开发实践

基于Spring Boot的银行客户管理系统开发实践 简介本资源是一套基于Spring Boot开发的银行客户管理系统完整毕业设计实现面向计算机相关专业本科生及Java初学者聚焦金融业务场景下的Web应用开发实践。系统涵盖客户服务交互、金融产品管理、账户卡片事务、储蓄与转账等八大核心模块覆盖真实银行业务流程助力学习者掌握Spring Boot企业级开发、MySQL数据建模及前后端协同开发能力。压缩包共1241个文件含193个Java后端逻辑文件、107个Vue前端组件、162个SVG图标资源、86个HTML页面及配套JS/CSS/SQL脚本整体大小为45.51MB预置build/run/install三类批处理脚本便于快速部署调试。已有68人下载学习提供可运行源码、结构清晰的模块化目录、完整数据库脚本及基础UI资源适合课程设计、毕设参考与Spring Boot实战能力提升。1. 项目概述与功能边界做毕业设计选“基于SpringBoot的银行客户管理系统”其实是一个很讨巧的决定。为什么这么说因为银行客户管理系统这个业务场景几乎覆盖了Java后端开发中所有核心必考知识点CRUD接口设计、数据库事务、权限认证、分页查询、日志记录、异常处理甚至还能延伸到缓存和消息队列。而Spring Boot恰恰把这些东西的搭建成本降到了最低——你不用像十年前那样配置一大堆XML一个starter依赖加几条注解就能把项目跑起来。对需要兼顾“工作量”和“完成度”的毕业设计来说这个组合堪称黄金搭档。先把这个系统到底要做什么讲清楚。银行客户管理系统本质上是银行内部用来管理客户信息、账户状态、交易流水的一个后台管理系统。它面对的角色一般有两类银行柜员和系统管理员。柜员负责日常的客户信息录入、资料修改、开户销户操作管理员则负责维护系统基础数据比如部门信息、员工账号分配、操作日志审计等。所以你在设计数据库表结构的时候脑子里必须时刻装着这两类角色的操作边界。从技术演进的角度看现在做这种管理系统Spring Boot几乎是默认选项。它内嵌Tomcat避免了繁琐的WAR包部署自动配置机制让数据源、ORM框架、日志框架开箱即用配合Spring Security或Sa-Token这类权限框架可以实现基于角色的访问控制。这套组合在真实企业项目中也是主流方案所以你在毕业答辩时完全有底气说“这个项目的技术栈与业界主流保持一致”。不过我需要提醒你一点银行客户管理系统虽然名字里有“银行”两个字但毕业设计不必真的去实现银行核心系统的清算、风控那些复杂逻辑。你只需要把客户信息管理、账户管理、交易流水记录这些基础业务闭环做好就已经达到毕业设计的体量要求了。与其追求功能大而全不如把每个模块的细节抠扎实——这才是答辩老师真正看重的东西。2. 技术选型与架构设计思路2.1 核心技术栈的选型依据Spring Boot的版本选择是很多新手踩坑的第一站。现在GitHub上拉下来的项目Spring Boot版本五花八门有2.7.x的有3.x的还有最新的3.4.x。我的建议是毕业设计稳妥起见选Spring Boot 2.7.x原因有三第一2.7.x是2.x系列的最终维护版本稳定性和资料丰富度都是最好的遇到问题一搜就有答案第二很多高校的Java课程还在讲JDK 8而Spring Boot 2.7.x完美兼容JDK 8你不需要折腾JDK版本切换第三网上现成的教程、博客大部分基于2.x系列你照着敲不容易卡壳。如果你选了Spring Boot 3.x那就必须搭配JDK 17及以上而且很多第三方starter的兼容性需要逐一确认。除非你追求技术新颖性否则没必要给自己增加这个额外的环境负担。ORM框架我用的是MyBatis-Plus而不是纯MyBatis。原因很简单MyBatis-Plus提供了内置的CRUD方法、分页插件、代码生成器能把重复性的单表操作代码量减少一半以上。对于客户信息这种典型的单表CRUD场景你甚至不用写SQL语句直接调用selectById、insert、updateById就行。但这不意味着你可以不懂SQL——复杂的联表查询、报表统计、事务操作还是得自己写XML或注解SQL。我建议你分页用MyBatis-Plus的分页插件但多表关联查询一定要手写SQL这样在答辩时被问到SQL功底时你才能对答如流。前端部分如果是传统单体项目直接用Thymeleaf模板引擎就够了服务端渲染模式Java和HTML融合在一起部署简单适合时间紧的毕业设计。如果你对前后端分离有兴趣前端用Vue 3 Element Plus后端提供纯JSON接口工作量会大一些但代码结构更清晰也更能展现你的工程化能力。两种方案我都做过时间充裕选前后端分离时间紧凑选Thymeleaf都拿得出手。2.2 分层架构与包结构设计Spring Boot项目虽然“约定优于配置”但代码组织还得遵循经典的分层架构。我记得第一次做项目的时候把所有类都塞在Controller里面Service层写得像摆设后来被带我的老工程师狠狠批了一顿。分层不是为了好看是为了可维护性——改一个需求你能快速定位到改动点而不是在几百行的大类里找半天。一个标准的模块包结构是这样划分的controller层只负责接收请求、参数校验、返回结果不写任何业务逻辑。service层业务逻辑的核心层处理客户开户时同时创建账户记录这种跨表事务操作。mapper层数据访问层继承MyBatis-Plus的BaseMapper负责和数据库打交道。entity层数据库表的实体映射类字段和表结构一一对应。dto层数据传输对象处理前端传入或后端返回的数据结构避免把数据库实体直接暴露给前端。config层配置类比如MyBatis-Plus分页插件配置、跨域配置、拦截器注册。common层统一返回结果封装、异常处理器、常量定义、工具类。vo层视图对象用于包装前端需要的展示数据比如客户详情页需要同时展示客户信息和账户列表就可以封装成一个CustomerDetailVO。每一层各司其职依赖方向从上往下单向流动。我见过不少项目为了图省事Controller里直接塞Mapper调用理由是“就一个查询没必要绕一圈”。短期看是省事了但后续如果这个查询需要加缓存、加权限校验、加数据脱敏你就要改所有调用点而不是只改Service层。毕业设计的代码量不算大但一个好的分层习惯会在未来的工作面试中给你加分。2.3 数据库表结构设计核心银行客户管理系统的数据库设计最少要包含这几张核心表客户信息表、账户信息表、交易流水表、用户表、角色权限表。这几张表不管怎么演化都是银行系统的基线。客户信息表customer的关键字段id主键、customer_no客户编号业务唯一键不能直接用数据库自增id当业务编号、name姓名、id_card身份证号、phone手机号、address联系地址、customer_type客户类型个人/企业、status状态正常/冻结/注销、created_time创建时间、updated_time更新时间。注意几个细节身份证号和手机号属于敏感信息虽然毕业设计不要求加密存储但至少要做到接口返回时脱敏——比如手机号只返回前3位和后4位。客户编号用独立字段不要用自增id因为真实的银行系统中客户编号是跨系统使用的有特定的编码规则比如“CUST”时间戳随机数。账户信息表account的关键字段id、account_no账号、customer_id关联客户表、account_type账户类型活期/定期/信用卡、balance余额decimal类型精确到小数点后两位、currency币种、open_date开户日期、status状态。交易流水表transaction_record的关键字段id、account_id关联账户表、transaction_type交易类型存款/取款/转账/消费、amount金额、balance_after交易后余额、description摘要、created_time交易时间。这里有一个非常重要的设计原则交易流水表只能增加不能修改和删除。所有余额变动必须通过流水记录来追溯。转账操作必须放在一个事务里——扣减转出方余额、增加转入方余额、写入两条流水记录任何一个环节失败整个事务回滚。这既是银行系统的铁律也是你答辩时展示事务能力的绝佳案例。用户表和角色权限表用来做登录认证和权限控制。用户表存用户名、密码、盐值、状态角色表存角色名称和角色编码用户角色关联表、角色权限关联表构成RBAC模型。毕业设计不需要做太细粒度的按钮级权限做到“柜员只能操作客户模块管理员还能操作用户模块”这种菜单级权限就够了。建表语句我建议自己手写不要依赖工具自动生成。写建表语句的过程中你会强迫自己思考字段类型选择、长度设置、索引设计、默认值设置这些细节正是面试官喜欢追问的点。sql文件放在项目的doc目录下提交代码时一并保留。3. 核心功能模块实现与代码定位3.1 客户信息管理的后端实现路线客户信息管理是系统的地基模块实现的是最简单的增删改查操作。但“最简单”只是表面真正面试时面试官一定会追问删除是物理删除还是逻辑删除分页查询怎么做条件筛选怎么组合物理删除就是一行DELETE语句把数据干掉逻辑删除是加一个deleted字段删除操作把它改为1后端查询时自动过滤掉已被删除的记录。银行系统的客户数据属于核心资产一般是禁止物理删除的——万一删错了数据就彻底没了。MyBatis-Plus对逻辑删除的支持非常友好只需要在实体类的删除标记字段上加TableLogic注解然后在配置文件中设置逻辑删除的值和未删除的值增删改查操作就会自动带上deleted条件。分页查询的实现在Spring Boot 2.x MyBatis-Plus环境下三步搞定第一步写一个MybatisPlusConfig配置类注册分页插件PaginationInnerInterceptor第二步Controller层接收pageNum、pageSize、keyword等参数第三步Service层封装一个带条件构造器的分页查询方法。核心代码大致是这个样子Service public class CustomerServiceImpl extends ServiceImplCustomerMapper, Customer implements CustomerService { Override public PageResultCustomerVO pageCustomers(Integer pageNum, Integer pageSize, String keyword, Integer status) { PageCustomer page new Page(pageNum, pageSize); LambdaQueryWrapperCustomer wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.isNotBlank(keyword), Customer::getName, keyword) .or().like(StringUtils.isNotBlank(keyword), Customer::getPhone, keyword) .eq(status ! null, Customer::getStatus, status) .orderByDesc(Customer::getCreatedTime); PageCustomer customerPage this.page(page, wrapper); // 把实体类转成VO对手机号、身份证号做脱敏处理 return PageResult.of(customerPage, this::convertToVO); } }这段代码有两个细节值得注意。or()的用法——eq和like条件用or连接时由于MyBatis-Plus的条件构造器是链式调用的如果不加or()方法多个条件默认是AND关系所以这里必须显式调用or()。另一个是手机号脱敏的处理convertToVO方法里写脱敏逻辑——这种代码看起来不起眼但放到答辩PPT里“我实现了敏感信息脱敏”这一条就是亮点。再说说DTO和VO的区别。客户表里如果有个customer_no客户编号前端新增客户时不需要传这个字段它应该由后端生成前端查询客户详情时可能希望返回登记时间和更新时间格式化之后的字符串。所以新增客户用CustomerCreateDTO更新客户用CustomerUpdateDTO查询结果用CustomerVO每个接口各用各的对象互不污染。字段多的时候确实显得啰嗦但字段一旦出现变更需求你绝对能体会到这种“啰嗦”的价值。3.2 账户开户与转账事务控制账户管理模块是系统里最能体现后端“功力”的部分。开户操作涉及两张表的写入插入一条账户记录同时更新客户表的状态或账户数量字段。如果这两步操作被人为拆成两次请求就面临数据不一致的风险。解决办法很简单Service层方法上加Transactional注解Spring声明式事务会自动管理Connection方法执行过程中任何一步抛异常整体回滚。但Transactional到底在做什么很多新手其实不清楚。它默认只对RuntimeException和Error回滚对受检异常比如IOException不回滚。所以当你在事务方法里写了try-catch吞掉异常时事务就不会回滚数据就脏了。如果你要吞掉异常务必在catch块里手动调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()强制标记回滚。转账操作的实现是财务管理系统的核心场景。模拟一个从A账户转100元到B账户的过程代码会包含这几步Transactional(rollbackFor Exception.class) public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) { Account fromAccount accountMapper.selectByIdForUpdate(fromAccountId); // 悲观锁 Account toAccount accountMapper.selectByIdForUpdate(toAccountId); if (fromAccount.getBalance().compareTo(amount) 0) { throw new BusinessException(余额不足); } fromAccount.setBalance(fromAccount.getBalance().subtract(amount)); toAccount.setBalance(toAccount.getBalance().add(amount)); accountMapper.updateById(fromAccount); accountMapper.updateById(toAccount); transactionRecordMapper.insert(new TransactionRecord(fromAccountId, TRANSFER_OUT, amount, fromAccount.getBalance())); transactionRecordMapper.insert(new TransactionRecord(toAccountId, TRANSFER_IN, amount, toAccount.getBalance())); }余额更新为什么要用selectByIdForUpdate这里用了悲观锁。假设两个请求同时查询A账户余额都是100元然后同时扣款就可能导致余额变成负数。SELECT ... FOR UPDATE会在数据库层面锁住这一行直到事务提交或回滚才释放锁从而避免并发超扣。这是一种比较保守的并发控制方案银行系统的转账场景更保守一点也说得过去。你可以在答辩时顺便提一句生产环境中还有乐观锁方案版本号机制和Redis分布式锁方案展示你的知识广度。另一个坑是BigDecimal的精度比较。compareTo返回-1、0、1分别表示小于、等于、大于。你如果直接用equals比较两个BigDecimal1.0和1.00是不相等的因为它们的scale不同——这种细节不看源码很难发现。金额运算必须用BigDecimal双精度浮点运算会造成精度丢失这是底线绝对不能用double或float。3.3 用户登录认证与角色权限控制登录认证模块是毕业设计里面最容易被判定为“工作量不足”的部分但同时也是最值得下功夫的部分。如果你只是写一个从数据库查用户名密码再对比的方法确实太简陋了。我建议至少实现以下三层第一层密码存储安全。明文存储密码是最低级的设计。正确的做法是“加盐哈希”存储Spring Security自带的BCryptPasswordEncoder就是干这个的。用户注册时把用户输入的明文密码用BCryptPasswordEncoder.encode()变成一串不可逆的哈希值存入数据库。登录校验时再调用matches()方法比对明文密码和哈希值是否匹配。这样即使数据库泄露攻击者也无法直接拿到用户明文密码。第二层会话管理。单体项目用Session就够了登录成功后把用户对象放到Session里后续请求通过拦截器校验Session是否存在。如果做了前后端分离用Token机制更合适——用户登录成功后后端生成一个Token可以用UUID也可以生成JWT返回给前端前端后续请求在请求头带上Token后端写个拦截器或过滤器统一校验。JWT方案的好处是无状态、不需要服务端存储会话记录但要注意JWT一签发生效除非加黑名单机制否则无法在到期前强制失效。毕业设计用UUID Token存Redis最简单省心——依赖少、逻辑直观、可控性好。第三层RBAC权限模型。RBAC即“用户-角色-权限”模型。一个用户可以被分配多个角色一个角色对应一组权限码。比如柜员角色拥有customer:add、customer:edit、customer:list权限管理员角色额外拥有user:add、user:delete权限。后端拦截器拦截所有需要鉴权的接口从用户的会话或Token中提取角色信息再查询对应权限码集合判断当前请求接口所需的权限码是否在集合中。拦截器的注册方式继承HandlerInterceptorAdapter或者实现HandlerInterceptor接口在WebMvcConfigurer中注册并定义好哪些路径需要拦截、哪些路径放行。放行的路径至少包括登录接口、静态资源路径、错误页面路径。拦截器代码看起来模板化但恰恰是Spring MVC的重要组件值得你花时间手写一遍。3.4 核心业务实体与工具代码示例理解了整体设计接下来看看具体的代码实现。我用MyBatis-Plus的实体类注解方式演示关键代码如果你是纯MyBatis方式思路类似只是SQL和ORM映射部分要自己写。先看客户实体类除了基本字段映射外还要注意逻辑删除和自动填充的注解Data TableName(customer) public class Customer { TableId(type IdType.AUTO) private Long id; private String customerNo; private String name; /** 身份证号需要脱敏处理 */ private String idCard; private String phone; private String address; private Integer customerType; private Integer status; TableField(fill FieldFill.INSERT) private LocalDateTime createdTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime; TableLogic TableField(select false) private Integer deleted; }自动填充的字段创建一个MetaObjectHandler的实现类在insertFill和updateFill方法里统一赋值Component public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createdTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updatedTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updatedTime, LocalDateTime.class, LocalDateTime.now()); } }这样设计的好处在于业务代码里调用insert和updateById时你完全不需要关注时间字段的赋值MyBatis-Plus会在底层自动完成。这也是在答辩中展示你对框架有深入理解的加分点——很多新手是手动set时间的。统一返回结果的封装类我一般这样写Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }全局异常处理用RestControllerAdvice加上ExceptionHandler做统一拦截。这样Controller里就不用写一整套错误处理逻辑了——业务异常统一抛BusinessException未知异常被全局兜底处理器捕获并返回统一错误结构前端拿到的永远是标准格式的JSON。这个结构可以提升整体的健壮性是任何商业项目都不可缺少的模块。4. 安全管理与防攻击设计4.1 接口层面的安全设计银行系统的数据太敏感了即使是毕业设计也必须完成基本的安全防护措施。安全不是某个单独模块的事而应该贯彻在系统设计的每个环节。XSS跨站脚本攻击的防护是Web应用最基本的安全策略。攻击者可能在表单输入框里写入script标签如果后端不做过滤直接存储到数据库前端渲染时就会执行这段恶意脚本。常用的防护手段是在前端对用户输入进行转义后端在接收参数时也做一遍过滤。对Spring Boot项目来说可以用一个过滤器拦截所有经过的请求将script、iframe等危险标签的尖括号转义成HTML实体。你可以在网上找到现成的工具类也可以理解原理后自己写一个。SQL注入攻击的防护用MyBatis框架基本已经屏蔽了这种风险——#{}是预编译占位符会使用PreparedStatement天然免疫SQL注入。但你如果为了某种便利在SQL中拼了字符串比如order by排序字段场景就可能被钻空子。解决办法是如果排序字段必须动态传入就把字段名做成一个白名单前端传什么先校验是否在白名单内。类似这种“虽然不是必要的但你做了就会更严谨”的细节很能体现开发者的安全意识。接口限流这块如果系统有并发压力可以在网关或拦截器层做简单的AOP限流。毕业设计落到代码层面可以用Google Guava的RateLimiter做简单的令牌桶限流。比如限制每个用户每分钟最多调用100次客户查询接口防止恶意刷接口。把这个写到文档里“我实现了接口限流保护”就是一个很能打的工作量亮点。4.2 数据脱敏与操作日志审计数据脱敏这个概念毕业设计里是最容易被忽略、但最能在答辩时拉开差距的点。客户信息里身份证号、手机号、地址都属于个人隐私数据理论上不应该明文返回给前端展示。最简单的脱敏方式在后端VO转换时做字符串替换public static String maskPhone(String phone) { if (StringUtils.isBlank(phone) || phone.length() 7) { return phone; } return phone.substring(0, 3) **** phone.substring(7); } public static String maskIdCard(String idCard) { if (StringUtils.isBlank(idCard) || idCard.length() 10) { return idCard; } return idCard.substring(0, 6) ******** idCard.substring(14); }这个实现很简单关键是主动去做、主动去讲——“客户列表和详情接口做了身份证号和手机号脱敏”这句话比“我实现了CRUD”高级太多了。操作日志审计是银行系统的硬需求。谁在什么时间对哪条客户数据做了什么操作要能完整追溯。实现方式也不复杂写一个AOP切面拦截所有加了自定义注解LogAnnotation的管理类接口在方法执行前后记录操作人、操作类型、接口耗时、IP地址、操作内容异步保存到操作日志表。这样不仅满足了审计要求还能顺带做一个“最近登录日志”的功能模块。数据权限和账号安全这块用户连续登录失败几次要锁定账号密码设定有效期定期强制更换这些都属于可扩展功能。你可以根据自己的时间安排决定做不做但在文档的“后续改进计划”里一定要提表明你有这个意识。4.3 密码加密与登录安全机制密码加密选型我前面提过BCryptPasswordEncoder这里再详细说一下为什么推荐它。BCrypt是密码学中一种加盐哈希算法最核心的特点是“自适应”——它允许你设定计算强度因子cost随着硬件性能提升你可以调高这个因子让哈希计算速度变慢从而增加暴力破解的成本。相比之下MD5和SHA系列算法是专为快速计算设计的暴力破解的成本相对低得多所以不推荐用于密码存储。登录安全机制的设计除了密码比对还要考虑几个细节登录失败处理——连续5次失败锁定账号15分钟防止暴力破解验证码——图形验证码或算术验证码可以防机器人批量登录配合Redis存储验证码并设置2分钟有效期会话安全——用户修改密码后把旧Token或旧Session置为失效防止旧的会话继续操作。这些点单独拎出来每一个都实现难度不高但组合在一起就是你系统与“别人毕业设计”拉开差距的地方。答辩时老师只要问“你是怎么保障系统安全的”你就有了一条完整的回答链路。5. 前后端联调与工程化实践5.1 环境准备与项目初始化先把开发环境理清楚。JDK 8、Maven 3.6、IDEIntelliJ IDEA社区版或专业版、MySQL 5.7或8.0、Navicat或DBeaver数据库连接工具。这几个安装包在你动手前搞定我建议直接去Oracle和Apache官网下载不要用来路不明的集成包。初始化项目的方式我推荐用Spring Initializr。在IDEA里“New Project”选择“Spring Boot Initializr”或者直接访问start.spring.io。注意SDK版本选择Java 8Spring Boot版本选择2.7.18。依赖项勾选Spring Web、MyBatis框架的starter如果初始界面上没有稍后在pom文件里手动添加、MySQL Driver、Lombok。Lombok一旦引入实体类的getter/setter、toString等模板代码就可以全部省略。但有一点必须提醒给IDEA安装Lombok插件同时在Build工具设置里确认注入了Annotation Processing否则项目启动时会报找不到getter方法——这是新手最常遇到的启动错误之一。pom文件里加MyBatis-Plus依赖、Spring Security依赖如果你决定用它做认证以及一些工具类依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency数据库配置写在application.yml里。注意时区配置serverTimezoneAsia/ShanghaiMySQL 8.0还要配useSSLfalseallowPublicKeyRetrievaltrue否则连不上。多环境配置建议直接拆成application-dev.yml和application-prod.yml一个连本地库一个连服务器库——这也是成熟项目的标准做法。5.2 本地开发与调试技巧开发调试阶段有句经验之谈先跑通一条最简链路再往上面加功能。别一上来就把所有功能做完再跑。最先做什么做一个“查询客户列表”的接口。Controller写一个Service写一个Mapper写一个数据库插两条测试数据把整个HTTP请求到JSON返回的链路跑通确认没有环境层面的问题然后再开始写其他功能模块。这个方法能帮你尽早暴露环境配置问题而不是到后期把所有错误信息堆在一起排查。调试技巧方面IDEA的断点调试一定得熟练。在业务代码的行号旁边点一下就打下断点然后以Debug模式启动项目在页面或Postman里发出请求代码走到断点处时会停下来你可以观察当前变量值、调用栈甚至在调试器里执行表达式。这比System.out.println打印日志定位问题高效太多。很多面试官在简历上看到“熟练掌握Debug调试”其实他们期待的就是你能讲清楚断点调试的基本操作和常见的Suspend策略设置。Postman或Apifox来测接口是必备技能。Postman里建立项目集合管理每一个接口的请求方式、请求头、请求体以及断言和测试脚本。Apifox比Postman对国内用户更友好支持接口文档同步、Mock数据、在线调试而且免费版功能够用。5.3 前后端联调时的若干经验前后端联调是项目中矛盾最集中的环节。如果你用Thymeleaf前后端是同一套代码联调问题相对少。如果选了Vue前后端分离跨域问题是第一道坎。解决办法在开发环境用Vue代理转发Vite或Webpack的proxy配置把/api前缀的请求转发到后端地址这样开发时浏览器看到的请求是同源的绕开CORS限制。生产环境把前端打包后的静态文件直接放到Spring Boot的static目录下就不存在跨域了。当然你也可以后端配置CORS跨域过滤器但对开发环境来说前端代理方案更优雅。接口返回值约定联调前务必定好格式。我用得最多的结构是{code: 200, message: 操作成功, data: {...}}。code不等于200就是业务异常或系统异常前端需要根据code做不同提示。code401表示未登录前端跳转登录页code403表示无权限前端提示无权访问。这个约定一旦定了就不要在开发中随意改否则前端要跟着改一晚上。前端调用接口时的加载状态、错误状态处理是影响体验的硬指标。请求发出时展示loading动画请求完成后关闭接口报错时统一弹Message提示而不是悄无声息。这些看似细枝末节却是前后端联调时最容易出现“低级冲突”的地方。6. 常见问题与排查心得实录6.1 数据库连接与启动类问题现象1项目启动时报Failed to configure a DataSource错误。排查思路第一步确认application.yml里有没有配数据源第二步确认URL里有没有带时区第三步确认MySQL服务有没有启动。这个报错90%以上都是配置写错或服务没启动用排除法一层层往下走问题很快就能定位。现象2启动时控制台报Unable to find a SpringBootConfiguration错误。遇到这个问题的原因往往是SpringBootApplication启动类放错了目录位置或者测试类扫描不到启动类。解决办法是检查启动类是否在包结构的根目录测试类能不能正确引用到它。Spring Boot的扫描机制是默认扫描启动类所在包及其子包所以启动类位置必须放在所有业务代码包的根目录。现象3MySQL 8.0版本和旧驱动的兼容问题。如果你用的是MySQL 8.0但pom里引的是com.mysql.cj.jdbc.Driver配置时必须写serverTimezone字段。新版驱动类名是com.mysql.cj.jdbc.Driver老的是com.mysql.jdbc.Driver。选错类名时启动后访问接口会报ClassNotFoundException或Unknown database之类的问题。6.2 MyBatis-Plus相关的坑关于分页插件MyBatis-Plus 3.x版本光加依赖不够必须注册分页插件拦截器。忘记注册时分页参数不生效查出来是全表数据——这是最隐蔽的坑。解决办法在配置类里加一个PaginationInnerInterceptor的Bean。关于逻辑删除逻辑删除在实体字段加了TableLogic注解之后MyBatis-Plus自动帮你把查询操作带上deleted条件。但如果你在XML里写了自定义SQL比如自定义联表查询那SQL里的deleted条件就需要自己加。不加的话自定义查询会把已逻辑删除的数据查出来和MyBatis-Plus自动管理的行为不一致。关于条件构造器用LambdaQueryWrapper时如果同时使用like和eq条件想拼出WHERE name LIKE %xx% OR status 1这样的结构直接链式调用会变成AND优先级高于OR。解决方案其实在上面演示过——在eq前面加.or()方法。这个例子很有代表性你用熟了LambdaQueryWrapper之后记得回去翻一遍MyBatis-Plus官方文档里关于and和or嵌套的所有说明能避开不少潜在的逻辑错误。关于乐观锁插件如果给某些数据表加了版本号字段要启用OptimisticLockerInnerInterceptor插件。不加这个插件执行updateById时版本号字段不会自动加1你写的乐观锁逻辑就形同虚设。这个插件的使用成本很低但很多人会遗漏。6.3 事务失效案例及根因分析事务失效是后端开发面试中最容易翻车的知识点。我从系统里挑两个典型场景细说。场景一自调用失效。一个类内部方法A调用了方法BB有Transactional注解但实际上方法B的事务不会生效。原因是Spring的事务通过AOP代理实现在同一个类内部调用方法时走的是this引用而不是代理对象AOP拦截器没有介入B方法的事务就形同虚设。解决办法是拆分成两个类或者在A里注入自身的代理对象Service public class CustomerService { Autowired private CustomerService self; // 注入代理对象 public void saveCustomerWithAccount(Customer customer, Account account) { self.doSave(customer, account); } Transactional(rollbackFor Exception.class) public void doSave(Customer customer, Account account) { customerMapper.insert(customer); accountMapper.insert(account); } }场景二异常被捕获吞掉。事务方法里用了try-catch捕获了异常并且没有再抛出事务就会认为方法执行成功从而提交最终导致数据不一致。这个问题的排查思路很清晰——在catch块中把异常打出来仔细看最稳妥的做法是catch之后throw new RuntimeException(e)重新抛出让事务管理器感知到异常。6.4 前端联调与部署上线问题前端最常见的问题是静态资源404。Thymeleaf模板页面放在src/main/resources/templates目录下但还是访问不到大概率是你忘了加spring.thymeleaf.prefixclasspath:/templates/这个配置。Spring Boot的Thymeleaf自动配置默认前缀就是classpath:/templates/Controller返回字符串视图名时它会自动去找对应模板。如果用了RestController那返回的就是纯JSON和模板引擎无关。这个区分要清楚。后端打包部署mvn clean package -DskipTests生成jar包后用nohup java -jar xxx.jar log.log 21 命令启动Linux服务器。Windows环境就直接java -jar跑起来。如果服务器上8080端口被占用可以启动时指定端口java -jar xxx.jar --server.port8081。这些基础操作按顺序做就行一旦跑通了整个流程后面再部署就不是难事。7. 毕业设计答辩要点与思路延伸7.1 如何向答辩老师介绍你的系统答辩时最忌讳从头到尾念PPT或者照着代码逐行念。建议按“问题驱动”的逻辑组织演示先抛出一个业务痛点——银行网点客户信息管理混乱、资料更新不及时、操作不可追溯然后引出你的解决方案——基于Spring Boot的银行客户管理系统接着演示核心业务流程最后重点讲两三个有深度的技术设计点。我建议你准备两三张核心图一张是系统架构图展示“前端→Controller→Service→Mapper→MySQL”的调用链路一张是核心业务流程图展示开户流程中客户信息校验、账户创建、流水记录、日志记录的先后顺序。手画图即可不用高大上的工具重点是把逻辑讲清楚。答辩老师关心的不是你的图多精美而是你脑子里有没有一张清晰的架构图。讲项目亮点时不要泛泛而说“我用了Spring Boot和MyBatis-Plus”而是说“我通过AOP切面统一记录了操作日志实现了敏感信息脱敏”。具体指标和数据更能打动人——客户查询接口平均响应时间120ms以内单表千万级数据量下分页查询稳定在200ms以内这些数据要提前压测出来写在答辩PPT里。7.2 系统演示时的常见翻车点我见过的答辩翻车现场90%都是“系统跑不起来”。所以答辩前务必确认三件事第一本地环境到答辩当天不要变数据库连接地址、账号密码、Redis连接信息都要稳定。最好准备一个测试环境所有演示数据都放在一个数据库里。第二演示用一套独立测试数据不要用生产数据。测试数据要覆盖不同状态、不同客户类型方便演示筛选和分页。比如客户状态要有“正常”和“冻结”账户类型要有“活期”和“定期”这样才能展示系统的完整功能。第三演示前先演练三遍以上。特别是转账这种涉及事务回滚的操作要准备一个“失败再恢复”的流程演示反而更能体现你对事务机制的理解。万一当场出现Bug不要慌先抛出一个合理的解释——“这里事务回滚了所以数据保持一致”既能化解尴尬还能加分。7.3 如何从毕业设计延伸到简历项目毕业设计做完不要急着删掉花点时间把它整理成简历上的项目经历。项目名称、技术栈、核心职责、项目亮点按这个结构写两到三行即可。这里的关键不是堆砌字数而是写清楚“你具体做了什么、用了什么技术、产生了什么效果”。比如“基于Spring Boot的银行客户管理系统”可以写成使用Spring Boot MyBatis-Plus MySQL开发实现了客户信息管理、账户开户、余额查询、转账操作等核心功能通过Spring AOP统一处理操作日志和服务端参数校验使用全局异常处理器规范了接口返回结构通过BCrypt算法实现密码安全存储。每个功能点都对应一项技术能力面试官看到的是“你能准确描述你的技术应用场景”。后续扩展方向我想提一点如果你有兴趣往更前沿的方向走这个项目可以作为一个基础底座逐步扩展成微服务架构。客户模块、账户模块、交易模块拆分独立服务用Nacos做注册中心和配置中心用OpenFeign做服务间调用用Seata处理分布式事务。这些技术栈在当前Java招聘市场中很主流而你已有的业务知识完全可以迁移过去。不过这是一条比较长的路先把这个单体系统做扎实再考虑微服务化也不迟。最后分享一个我个人的经验毕业设计做完后把整个项目的开发过程写成文章或记录在博客里包括需求分析、表结构设计、核心代码实现、遇到的问题和解决过程。这不仅是你找工作时的谈资更是回顾技术成长最珍贵的时间线。写的过程中你会发现自己当初很多“想当然”的设计其实有更优的方案——这本身就是技术的进步。本文还有配套的精品资源点击获取
返回列表