SSM咖啡店后台管理系统:从三层架构到JSON接口改造实战 简介面向计算机相关专业毕业设计的Java连锁咖啡店后台管理系统源码包围绕门店、订单与会员等业务场景组织项目结构适合正在完成毕设、课程设计或希望通过SSM架构提升Java Web开发能力的在校生与初级开发者。压缩包共25个文件以13个Java源码文件为主体配合9个XML配置文件、properties系统配置与JSP页面构成一套可直接运行的Web后端管理项目骨架整体压缩后仅19KB下载后便于快速解压和本地部署查看。目前已有112人学习浏览常见于毕设选题、课程大作业和初期项目演示等场景也适合作为入门学习和二次开发的基础工程。资源本身可用作毕业设计初始代码或课程作业蓝本项目代码经过运行验证既支持直接启动查看效果也便于在此基础上扩展会员维护、订单处理、门店管理等模块。从Java源码分层、XML配置到JSP页面交互的完整链路有助于理解Java Web项目开发流程为后续功能迭代和答辩讲解提供实际参考。1. 连锁咖啡店后台管理系统一份能跑通全流程的 Java 单体项目样板别小看这份毕业设计级别的压缩包它不是一个只有登录和一堆假页面的教学示例而是一套能把门店、商品、订单、会员全部串起来管理跑的连锁咖啡店后台管理系统。整个后台基于 Java 编写采用 SSM 框架组合前端页面用 JSP 渲染打包后扔进 Tomcat 就能部署代码量不算大但三层架构完整接过 Java 老项目的一线开发打开 pom.xml 就能确认它是真能运行的完整软件包。这套源码适合三类人拿 Java 做毕业设计、需要完整系统作为参考的学生想搞明白 Spring、SpringMVC、MyBatis 三者如何协作的初级开发者以及要给中小型门店类业务做后台初始版本、想直接在一套真实代码上做二次开发的人。它没有微服务网关也没有分布式事务就是一个老老实实的单体后台——正因为干净反而比微服务大项目更适合作为第一份完整工程来阅读、运行和改造。2. 技术栈与工程结构从 pom.xml 到 webapp 的项目骨架拆解2.1 SSM 选型为什么要用这套组合而不是直接 Spring Boot现在新建 Java 项目大多数人直接选 Spring Boot但很多院校和企业遗留项目仍然跑在 SSM 上。把这三者拆开看Spring 负责 Bean 的装配和事务管理SpringMVC 负责 HTTP 请求的路由和参数绑定MyBatis 负责 SQL 映射与数据库访问。这个项目选择 SSM最大的价值在于每一层都要显式配置你被迫去读 spring-dao.xml、spring-service.xml、spring-mvc.xml 这三个配置文件运行链路会变得非常透明。如果用 Spring Boot很多配置被自动装配吞掉了反而不容易理解一个请求从浏览器到数据库再返回页面经过了什么。我一般会建议第一次接触完整项目的人先啃一遍 SSM 代码等清楚了三层架构再回去用 Spring Boot你会觉得自动装配更像“后悔药”而不是黑匣子。从资源包里的 crm-coffee-ssm.iml 文件也能看出这是一份用 IDEA 打开的 Maven 单模块工程源码包下面直接就是 pom.xml 和 src 目录部署产物是 war 包而不是 Spring Boot 那种可执行 jar。2.2 目录结构一眼看完整个工程打开源码后标准的 Maven 工程布局长这样这里的目录是按这个业务场景最常见的划分补齐的压缩包内的实际包名可能略有出入但结构基本一致crm-coffee-ssm ├── pom.xml └── src ├── main │ ├── java │ │ └── com/coffee/crm │ │ ├── controller # 控制器层接收 HTTP 请求 │ │ │ ├── LoginController.java │ │ │ ├── StoreController.java │ │ │ ├── ProductController.java │ │ │ └── OrderController.java │ │ ├── service # 业务层事务边界都在这里 │ │ ├── dao # MyBatis 的 Mapper 接口 │ │ ├── entity # 数据库表的实体类 │ │ ├── common # 分页对象、统一的返回包装类 │ │ └── interceptor # 登录拦截器 │ ├── resources │ │ ├── jdbc.properties # 数据库连接配置 │ │ ├── spring-dao.xml # 数据源与 MyBatis 配置 │ │ ├── spring-service.xml # 事务配置 │ │ ├── spring-mvc.xml # SpringMVC 配置 │ │ ├── mybatis-config.xml # MyBatis 全局配置 │ │ └── mapper # Mapper 接口对应的 XML │ └── webapp │ ├── WEB-INF │ │ ├── web.xml │ │ └── jsp # 后台页面 │ ├── static │ │ ├── css │ │ └── js │ └── index.jsp └── sql # 建表脚本有的版本放在压缩包根目录注意 sql 目录这个压缩包有没有带建表脚本直接决定了你动手跑的速度。拿到资源后第一件事不是看业务代码而是找 .sql 文件如果没带就从 entity 类里每个字段反推 CREATE TABLE字段名和注释都对得上二十分钟能建完。2.3 三个 spring 配置文件各自管什么SSM 的启动入口是 web.xml。它会在 Tomcat 启动时加载 ContextLoaderListener把 spring-dao.xml 和 spring-service.xml 拉起来再加载 DispatcherServlet 对应的 spring-mvc.xml。三份文件分工非常清晰spring-dao.xml负责 dataSource、SqlSessionFactoryBean、Mapper 接口扫描。数据库连接信息从 jdbc.properties 里读取。spring-service.xml配置 DataSourceTransactionManager开启注解事务让 service 层 Transactional 生效。spring-mvc.xml扫描 controller开启注解驱动配置 JSP 视图解析器和静态资源放行。!-- spring-mvc.xml 精简示例 -- mvc:annotation-driven/ mvc:resources mapping/static/** location/static// context:component-scan base-packagecom.coffee.crm.controller/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp// property namesuffix value.jsp/ /bean没部署过的同学容易在这里入坑InternalResourceViewResolver 的 prefix 指向 /WEB-INF/jsp/意味着 Controller 返回的字符串比如 store/list最终会去访问 /WEB-INF/jsp/store/list.jsp。这个目录不能让用户通过 URL 直接访问只能经控制器转发这是一个常见设计同时也解释了很多 404 的来源。2.4 pom.xml 依赖是它撑起了整套运行环境Maven 单模块工程的依赖都集中在 pom.xml我挑几个关键依赖说明一下版本号每个下载版本略有差异以压缩包里的 pom.xml 实际配置为准spring-webmvcSpringMVC 框架处理请求映射与参数绑定。spring-jdbc / spring-tx数据源支持与声明式事务。mybatis / mybatis-springORM 映射以及和 Spring 的桥接。mysql-connector-java数据库驱动MySQL 8 驱动的类名是 com.mysql.cj.jdbc.Driver。javax.servlet.jsp.jstlJSP 页面里做集合循环、格式化输出。jackson-databind如果返回 JSON 接口Controller 会用到的序列化库。读完依赖你会发现没有引入 Spring Boot 相关的 starter所以部署方式也是传统的编译成 war 包丢进 Tomcat 的 webapps 目录而不是内嵌容器直接 java -jar。这一点直接影响你后面跑项目的步骤不要照着 Spring Boot 的习惯执行。还有一个 Maven 细节打包时默认不一定会把 resources 目录下的 mapper XML 拷进 warpom.xml 里往往需要显式配置构建资源目录否则部署后所有 Mapper 的 SQL 都找不到这个问题很多新手都会踩一遍。3. 核心业务模块拆解从登录拦截到订单落库的完整链路3.1 登录鉴权拦截器让未登录请求无处可逃后台管理系统的第一道防线是登录拦截。这个项目的处理方式是定义一个 HandlerInterceptor在 preHandle 里检查 session 中的登录用户对登录接口和静态资源放行其他请求一律重定向到登录页。代码结构大概是public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 登录接口与静态资源不需要校验登录状态 String uri request.getRequestURI(); if (uri.contains(/login) || uri.startsWith(/static/)) { return true; } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; } }这段代码的逻辑很直白先从 HttpSession 里取 loginUser 属性取不到就判定未登录发送重定向。要注意拦截路径写法项目里如果同时存在 GET 和 POST 的 /login只放行路径即可而 /static/ 前缀是为了保证页面里的 css、js 请求不被拦截。因为 JSP 页面引用的是相对路径静态资源一旦拦截器把静态请求也挡住页面就会失去样式看起来像“网页坏了”实际是没放行。拦截器写好后要注册到 SpringMVC常见做法是在 spring-mvc.xml 里做如下配置登录页和静态目录排除在外mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/static/**/ bean classcom.coffee.crm.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors参数说明mapping path 表示拦截范围/** 是拦截所有路径exclude-mapping 是白名单优先级高于拦截规则。一个很容易犯的错是在拦截器里硬编码路径前缀目录调整后忘了同步改 XML结果某天所有请求都跳登录。我的排查习惯是先看 session 变量名是否和前端存的一致再检查 XML 里到底排除了哪些路径。3.2 门店管理分页查询与动态 SQL 的常见姿势门店列表是后台系统的经典功能核心是分页和条件筛选。Controller 接收页码和可选的门店名称、城市参数调用 Service 层查出一页数据放进 PageBean 对象最后返回 JSP 渲染。控制器部分通常是Controller RequestMapping(/store) public class StoreController { Autowired private StoreService storeService; RequestMapping(/list) public String list(Integer pageNum, String name, String city, Model model) { if (pageNum null || pageNum 1) { pageNum 1; } PageBeanStore page storeService.findByPage(pageNum, 10, name, city); model.addAttribute(page, page); return store/list; } }这段代码里的 PageBean 是手写的分页包装类常见字段包括 pageNum、pageSize、totalCount、list。手写分页的好处是不依赖插件、SQL 里手动拼 LIMIT #{offset}, #{pageSize}特别适合拿来学 MyBatis 的参数绑定缺点是每个查询都要自己算 offset我一般会把这个计算放到 Service 层统一处理避免在 Controller 里重复写。对应的 Mapper XML 是本项目的学习重点它演示了 MyBatis 动态 SQL 怎么按条件拼查询select idfindByPage resultTypecom.coffee.crm.entity.Store select * from tb_store where if testname ! null and name ! and name like concat(%, #{name}, %) /if if testcity ! null and city ! and city #{city} /if /where order by create_time desc limit #{offset}, #{pageSize} /select参数说明where 标签会自动去掉多余的 and避免出现 where and name xxx 这种语法错误if 标签判断的是传入的查询条件name 为空时该段 SQL 不拼接。limit 里的 offset 是 (pageNum-1) * pageSize由 Service 算好再传进来。这种写法需要留意的坑是 resultType 的包名要写实体类的全限定名如果 TypeAliases 没配置别名写错一行就会在运行时抛绑定失败。3.3 商品管理图片上传与商品上下架咖啡单品需要配图片SSM 项目里最常见的上传方式是 Controller 接收 MultipartFile把文件写到服务器的 upload 目录再把访问路径存进数据库。小规模后台不会接对象存储本地目录够用但要注意目录必须是真实存在的物理路径以及重启应用的绝对路径要稳定。RequestMapping(/add) public String add(RequestParam(file) MultipartFile file, Product product, HttpServletRequest request) throws IOException { if (file ! null !file.isEmpty()) { String realPath request.getServletContext().getRealPath(/upload); File dir new File(realPath); if (!dir.exists()) { dir.mkdirs(); } String fileName System.currentTimeMillis() _ file.getOriginalFilename(); file.transferTo(new File(dir, fileName)); product.setImagePath(/upload/ fileName); } productService.add(product); return redirect:/product/list; }这个方法的处理逻辑是从请求里取出名为 file 的文件如果非空就拿到当前应用部署环境下的 /upload 真实路径创建目录后把文件写入文件名加时间戳前缀是为了防止重名覆盖。图片路径存的是应用内的 /upload/xxx.jpg页面里直接用基于根路径的 img 标签即可显示。参数说明MultipartFile 需要 SpringMVC 在配置里提供 multipart 解析器比如 CommonsMultipartResolver否则取不到文件getRealPath 返回的是 Tomcat 部署目录下的路径本地运行和打包部署后路径会变化所以不要在代码里写死绝对路径。如果上传后图片访问 404大概率是静态资源映射把 /upload/** 拦掉了需要在 spring-mvc.xml 里加一条 mvc:resources mapping。3.4 订单模块一次下单同时写主表与明细表订单是后台系统里最容易出事的模块因为一次下单至少要写订单主表、订单明细表、扣减库存三件事任何一个失败都会留下脏数据。这个项目使用 Spring 的声明式事务来解决在 Service 方法上加 Transactional让三个写操作在同一事务里提交Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Transactional(rollbackFor Exception.class) Override public void createOrder(Order order, ListOrderItem items) { // 1. 插入订单主表回填自增主键 orderMapper.insertOrder(order); // 2. 遍历明细绑定 orderId 后插入明细表 for (OrderItem item : items) { item.setOrderId(order.getId()); orderMapper.insertOrderItem(item); } // 3. 扣减门店对应商品的库存 orderMapper.reduceStock(order.getStoreId(), items); } }rollbackFor Exception.class 是这段代码的关键参数它告诉 Spring 任何异常都回滚包括编译期异常。如果漏写 rollbackFor默认只会回滚 RuntimeException很多数据库异常在调用层被包装成检查性异常后事务就可能没有正确回滚数据就会出现“主表写进去了、明细没写”的惨状这是事务相关的血泪经验。库存扣减这里用 reduceStock 表示一条动态 SQL实际项目中会写成批量 UPDATE避免逐条扣减带来的性能问题。需要注意的是事务要放在 Service 层而不是 ControllerController 调 Service 时事务代理才会生效如果 Service 内部通过 this 自调用另一个加注解的方法事务会失效因为代理没有经过。遇到订单数据对不上时优先检查是不是把 Transactional 写到了私有方法或自调用场景里。4. 本地跑起来数据库初始化、IDEA 配置与 Tomcat 部署步骤4.1 环境版本对照装错版本是最大的玄学问题SSM 老项目对环境版本特别敏感JDK、Maven、MySQL、Tomcat 任何一个版本跨度太大都可能启动失败。按这个项目常见配套我列了一张对照表你照着准备最省事组件建议版本说明JDK1.8项目基于 Java 8 开发换 11 以上可能遇到反射权限问题Maven3.6.x与 IDEA 自带 Maven 兼容性最好MySQL5.7 或 8.0驱动类名不同连接串写法也不同Tomcat8.5 或 9.0适配 Spring 5.x避免使用 Tomcat 7IDEA2020 及以上打开 pom.xml 即可自动识别 Maven 工程提示分发给多人协作时尽量统一这五个版本。我遇到过项目在 Tomcat 8.5 上一切正常换 Tomcat 10 后直接报 Servlet 相关类找不到这是因为 Tomcat 10 的 Servlet API 包名从 javax 换到了 jakarta而 Spring 5 还在用 javax跑不起来。4.2 初始化数据库建库、建表、造数据第一步是建库和建表。如果压缩包带了 .sql 脚本直接执行如果没有按 entity 实体类逐张建表常用工具是 Navicat 或者命令行。通常的建表顺序是用户表、门店表、商品分类表、商品表、订单主表、订单明细表因为订单表有外键依赖门店和商品。-- 创建一个咖啡店后台专用数据库 create database if not exists crm_coffee default character set utf8mb4; use crm_coffee; -- 用户表管理员/店长共用 create table tb_user ( id int primary key auto_increment, username varchar(50) not null, password varchar(64) not null, role varchar(20) default admin, create_time datetime default current_timestamp ) engineinnodb default charsetutf8mb4;执行完脚本后记得插入一条初始账号比如 admin / 123456。如果登录时代码里做了 MD5需要把加密后的密文填进表里不能直接存明文。判断方式很简单找登录逻辑里有没有 MD5 相关的调用有的话就去生成一条相同算法的密文再插入否则登录永远提示账号或密码错误。4.3 修改 jdbc.properties时区、编码与密码数据库准备好之后改资源目录下的 jdbc.properties。这是项目能否连接数据库的核心文件常见内容如下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/crm_coffee?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password123456逐个参数说明driver 用 com.mysql.cj.jdbc.Driver这是 MySQL 8 的驱动类名如果本机是 MySQL 5.7 可以把类名换成 com.mysql.jdbc.Driverurl 里的 characterEncodingutf8 保证中文写入不乱码serverTimezoneAsia/Shanghai 解决 MySQL 8 与时区相关的报错useSSLfalse 是关闭安全连接提示避免启动日志里刷 SSL 警告。password 要改成你本机数据库的密码这一步漏改启动必报错。如果连接 5.7 版本url 里的 serverTimezone 可以保留也可以去掉不影响运行但 driver 类名必须与 jar 匹配MySQL 5.7 用 com.mysql.jdbc.Driver 更稳。4.4 IDEA 导入项目与启动步骤在 IDEA 里 File - Open 选择解压后的目录等待 Maven 自动下载依赖。如果右下角弹出 Maven 项目提示全部导入再检查右侧 Maven 窗口里是否出现依赖列表。导入完成后按下面步骤配置打开 Project Structure把 Project SDK 设置为 JDK 1.8Language Level 选 8。打开 Run/Debug Configurations新增 Tomcat 本地服务器配置Deployment 里添加该项目的 war exploded 包。配置 Application context 为 /crm-coffee这样启动后访问地址是 http://localhost:8080/crm-coffee。点击 Debug 启动Tomcat 日志出现 Started 之后打开浏览器访问登录页。需要提醒的是IDEA 里直接跑 war exploded 而不是 war 包改动 JSP 页面可以不用重启开发体验会好很多。老项目第一次启动慢是正常的Maven 在拉依赖时会卡在下载环节换成阿里云镜像是个常规自救办法。4.5 命令行启动不想用 IDEA 就在终端跑如果不想依赖 IDEA可以用 Maven 插件直接起项目。这个项目是传统 war 工程pom.xml 里一般配置了 tomcat7-maven-plugin 或类似插件执行以下命令之一mvn clean tomcat7:runmvn clean package # 打包完成后把 target/crm-coffee.war 复制到 Tomcat 的 webapps 目录tomcat7:run 适合本地快速验证但它内嵌的是 Tomcat 7与新版 Tomcat 有细微行为差异页面展示以正式部署到 Tomcat 8.5 为准。打包命令会先跑测试如果单元测试失败可以加上 -Dmaven.test.skiptrue 跳过注意排除掉代码里可能写死的测试库连接。提示命令行启动时先确认 8080 端口没被占用Windows 上用 netstat -ano | findstr 8080 查看查到 PID 后结束进程可以避免地址被占用导致的启动失败。5. 避坑指南SSM 项目最常见的五个报错与排查记录5.1 部署后访问首页直接 404现象Tomcat 启动没有任何异常浏览器访问 http://localhost:8080/crm-coffee 却返回 404。原因Application context 配的路径跟实际访问路径不一致或者项目没有把 index.jsp 设置成欢迎页。老项目还常见 web.xml 里没有配置 welcome-file根路径就不知道渲染哪个页面。解决确认 IDEA 里 Tomcat 的 Application context 填的是 /crm-coffee再检查 web.xml 是否有 welcome-file 配置没有的话补上最后确认 index.jsp 在 webapp 根目录而不是 WEB-INF 下WEB-INF 下的页面无法被浏览器直接访问。5.2 Invalid bound statement (not found) 调接口就报错现象项目能启动但一调用某个查询接口就抛 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。原因Mapper 接口类被 Spring 扫描到了但对应的 XML 文件没有被 MyBatis 加载。常见原因是 resources 目录下的 mapper XML 路径与 Mapper 接口的包名不对应或者 spring-dao.xml 里的 mapper-locations 写成了 classpath:mapper/*.xml 与实际路径不符。解决检查 mapper XML 的 namespace 是否等于 Mapper 接口的全限定名检查 XML 里每条 select 的 id 是否等于接口方法名再检查 pom.xml 里是否漏了把 resources 目录下的 XML 一起打进包Maven 有时会默认过滤非 properties 文件需要加一条资源包含配置。5.3 数据库连接报错 Communications link failure 或时区异常现象Tomcat 启动后数据源初始化失败日志出现 Communications link failure或者提示 The server time zone value xxx is unrecognized。原因八成是 MySQL 8 的时区问题或者数据库服务没启动、账号密码不对。MySQL 8 对时区要求比较严格连接串里不写 serverTimezone 就会在拿连接时抛时区异常。解决按前面 jdbc.properties 的写法补 serverTimezoneAsia/Shanghai。如果加了还是连不上用命令行 mysql -u root -p 亲手确认账号密码排除密码里包含特殊字符导致 properties 解析错乱的情况特殊字符密码建议在 properties 里使用转义或先改成简单密码测试连通性。5.4 页面中文全部变成问号现象登录成功进后台所有中文菜单、列表数据都显示成问号但数据库里存的中文是正常的。原因页面编码和数据库连接串编码不匹配。常见是页面本身是 UTF-8但 Tomcat 默认用 ISO-8859-1 解析请求参数又或者 jdbc.url 缺了 characterEncodingutf8写入时就变成乱码。解决双重修复。第一在 web.xml 里配置 CharacterEncodingFilter 并把 encoding 设为 UTF-8注意该 Filter 必须放在最前面第二确认 jdbc.url 带上了 characterEncodingutf8同时表结构使用 utf8mb4 字符集。改完后清空浏览器缓存再次测试。5.5 上传商品图片后 img 标签 404现象表单提交成功数据库里 imagePath 也有值浏览器里图片就是加载不出来F12 看请求返回 404。原因SpringMVC 的 DispatchServlet 拦截了 /upload/ 路径请求静态资源被当作 Controller 映射去处理找不到对应处理器就返回 404。解决在 spring-mvc.xml 里加一条静态资源映射配置如下mvc:resources mapping/upload/** location/upload//参数说明mapping 是浏览器访问的外部路径location 是文件实际存放位置对应的 Web 根路径。注意 location 末尾要带斜杠否则 Spring 会拼出错误的目录。还有一个隐蔽坑本地用 IDEA 跑时 getRealPath(/upload) 指向的是 target 目录下的临时位置清空 target 后文件会丢图片路径又会失效所以文件存储位置最好抽出来配成外部目录这是很多老项目的遗留问题。6. 二次开发把 JSP 渲染拆成 JSON 接口的前后端分离改造6.1 改造目标与新增接口层拿到这个 SSM 项目后最常见的第一个改造需求不是加功能而是把 JSP 模板渲染改成前后端分离页面不再用服务器端拼接而是用 Vue 或原生 JS 调用 JSON 接口。改造有个核心原则——不动 Service 和 Mapper 层只在 Controller 层新增独立的 API 控制器让业务逻辑复用原有代码改造风险会降到最低。改造前先新增一个统一返回包装类 Result约定三个字段code 表示状态码msg 是提示信息data 放业务数据。它的作用相当于把 Model 和视图解耦掉前端拿到固定结构的 JSON 后自己决定怎么渲染。这一步虽然是重复劳动但接口规范一旦统一后续加接口就不用每处各写各的返回结构。6.2 新增 API 控制器与验证方法按上面的思路我一般会在 controller 包下新建 StoreApiController复用原有 StoreService 的分页逻辑返回 JSONController RequestMapping(/api/store) public class StoreApiController { Autowired private StoreService storeService; RequestMapping(/list) ResponseBody public Result list(Integer pageNum, Integer pageSize) { if (pageNum null) { pageNum 1; } if (pageSize null) { pageSize 10; } PageBeanStore page storeService.findByPage(pageNum, pageSize, null, null); return Result.success(page); } }这段代码与原来的 JSP 控制器差别只有两处一是类上标注了不同的 RequestMapping 前缀二是方法上加了 ResponseBody让 SpringMVC 把返回值序列化成 JSON 而不是解析成视图名。改造后原有的 store/list 接口继续保留JSP 页面和新的接口可以共存过渡期互不影响。验证接口是否可用启动后用 curl 测一下即可curl http://localhost:8080/crm-coffee/api/store/list?pageNum1pageSize5返回 JSON 里能看到门店列表数据就说明接口改造成功。接着把前端页面放进 webapp 的 static 目录用 fetch 请求这个接口页面就可以独立开发了后端不再需要碰 JSP。需要注意跨域问题如果前端单独跑在 8081 端口后端要在 spring-mvc.xml 里配置 CORS 过滤器否则浏览器会把请求拦掉。我从一开始拆这类项目就养成了习惯先把老路径保留住新增平行的 API 层等前端完全迁移完再考虑删旧控制器。这套渐进式改造的办法帮我避免了好几次“页面改到一半发现数据查不出来”的翻车事故从那以后我每次接手老项目都强制走一遍“复用 Service、新增 Controller、保留旧接口”的流程。希望帮到你如果你想给自己的毕设锦上添花这个 JSON 改造配合 ECharts 报表就能让整套系统从“能跑”升级到“能讲清楚技术点”。本文还有配套的精品资源点击获取