SSM框架电脑配件商城系统:Java Web核心考点全解析
简介一套基于SSM框架的电脑配件销售商城Java项目面向毕业设计、课程实战及SSM入门学习解决商城系统从后台管理到前台购物的完整业务闭环。压缩包共1344个文件、22.21MB不止包含源码和数据库脚本还包含前端页面与配置文档主要类型为Java类132、JSP页面156、JavaScript脚本359、CSS样式166、图片素材png/jpg/gif等以及XML配置可直接导入IDEA或Eclipse运行。目前已有133人学习下载适合需要成套参考的开发者。项目采用SpringSpringMVCMyBatisMavenMySQL架构后台管理涵盖用户、商品分类、商品上下架、库存调整、订单管理、轮播图与新闻资讯前台则支持注册登录、商品推荐与详情、收藏、购物车、立即购买、评论、余额/积分充值、支付和个人中心。附带的数据库脚本和毕业论文便于理解SSM分层结构与电商业务逻辑也能辅助二次开发和毕业答辩。整体结构完整适合作为毕业设计演示项目。1. 基于SSM的电脑配件销售系统一个把Java Web核心考点全串起来的毕设样本每到毕业季和招聘季基于SSM框架实现的电脑配件销售系统这个标题都会在Java相关搜索里高频出现。它不是炫技项目却把Spring、SpringMVC、MyBatis完整跑在了一个B/S架构的商城上商品展示、分类检索、购物车、订单、后台管理每一块都对应Java就业面试里的高频考点。对正在选毕设题的学生它比普通管理系统多一层业务复杂度又比微服务商城轻很多对要快速交付原型的外包开发者它是一套能改一改就上线的底座。下面按真实落地顺序来先拆架构和表设计再讲源码怎么跑起来然后走读核心模块最后把启动和上线阶段的坑一次说清。2. SSM框架与B/S架构这套组合为什么至今仍是商城项目的默认起点2.1 Spring、SpringMVC、MyBatis的分工谁管对象、谁管请求、谁管数据SSM这个缩写出现在无数java面试题里但很多人只是背概念没想过三个框架为什么这样搭。Spring最核心的是IoC容器和AOP所有Service、Controller、Mapper对象的创建和依赖注入都归它管Controller里不需要手写new Service()一个Autowired就完成了。SpringMVC只处理Web层从DispatcherServlet分发请求到Controller方法再到视图渲染它把HTTP协议和Java方法之间的转换包掉了。MyBatis负责数据库增删改查属于半自动ORMSQL由开发者自己写框架只做参数映射和结果集映射——商城这类查询条件多、需要动态拼SQL的业务用半自动比全自动好控制得多。pom.xml里这组依赖我配过很多次可以直接抄dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.3.27/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.13/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependencyspring-webmvc这个坐标会把spring-core、spring-beans、spring-context等基础模块连带拉进来不需要逐个声明。mybatis-spring是Spring和MyBatis的桥接包没有它SqlSessionFactoryBean和SqlSessionTemplate就注入不进来这是新手经常漏掉的一个依赖。版本上Spring 5.3.x配JDK 1.8是我验证过最稳的组合纯毕设和外包交付没必要追新。为什么标题不直接用Spring Boot既然写了SSM框架就说明选型走的是传统SSM路线。Spring Boot开发快但自动配置把很多细节封装掉了面试官问到底层Bean怎么注册、事务怎么切的时候容易答不上来。SSM要求手动配事务、手动配视图解析器、手动写web.xml反而把Java Web的路径趟清楚了。对毕设来说这种麻烦恰恰是答辩素材。2.2 B/S架构的请求流转一次商品查询背后经过了哪些层B/S架构就是浏览器/服务器架构客户端只有浏览器业务逻辑和页面全部在服务端维护。这套商城系统的请求链路是用户在浏览器输入URL → Tomcat接收HTTP请求 → web.xml里注册的DispatcherServlet拦截 → HandlerMapping找到对应Controller方法 → Controller调用Service接口 → Service调用Mapper接口 → MyBatis执行SQL并映射结果 → 逐层返回后由视图解析器渲染JSP → 浏览器展示页面。商品列表查询的Controller长这样Controller RequestMapping(/product) public class ProductController { Autowired private ProductService productService; RequestMapping(/list) public String list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 8) Integer pageSize, Model model) { PageInfoProduct page productService.queryPage(pageNum, pageSize); model.addAttribute(page, page); return product/list; } }Controller把类注册为Spring的BeanRequestMapping(/product)定义类级别的URL前缀方法映射/list最终访问路径就是/product/list。方法返回的product/list是逻辑视图名对应webapp下WEB-INF/views/product/list.jsp前后缀在spring-mvc.xml里通过InternalResourceViewResolver配置。这条链路每个环节职责单一出错好定位页面不对看JSP请求不对看Controller数据不对看Mapper SQL。视图解析器配置是B/S架构里特别容易忽略的一块少了它返回字符串时直接404bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views/ / property namesuffix value.jsp / /beanprefix和suffix合起来Controller返回的product/list就会解析到/WEB-INF/views/product/list.jsp让Tomcat渲染。把JSP放进WEB-INF下有个额外好处浏览器无法直接通过URL访问只能经过Controller转发比放在webapp根目录更安全目录结构也清爽。2.3 数据库表设计配件商城最核心的表与字段取舍电脑配件商城最核心的五张表是用户表、分类表、商品表、订单主表、订单明细表。购物车可以先走Session不做表但如果要做登录后合并购物车就需要一张购物车表这个取舍在第4章展开。先看商品表它是整个系统的数据核心CREATE TABLE product ( id INT(11) NOT NULL AUTO_INCREMENT COMMENT 商品ID, name VARCHAR(100) NOT NULL COMMENT 配件名称, category_id INT(11) NOT NULL COMMENT 分类ID, price DECIMAL(10,2) NOT NULL COMMENT 售价, stock INT(11) NOT NULL DEFAULT 0 COMMENT 库存, image VARCHAR(255) DEFAULT NULL COMMENT 图片路径, description TEXT COMMENT 参数描述, sales INT(11) NOT NULL DEFAULT 0 COMMENT 销量, status TINYINT(4) NOT NULL DEFAULT 1 COMMENT 1上架 0下架, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;价格用DECIMAL不用DOUBLE或FLOAT是电商项目的基本常识——二进制浮点数存0.1会有精度误差算总价迟早对不上。status字段做上下架逻辑而不是删记录能保留历史订单关联的商品信息。image只存相对路径图片文件放Tomcat的webapps目录或独立图片服务器不要往数据库里塞二进制。订单主表和明细表是一对多关系主表存整单状态和总金额明细表存具体买了什么、当时单价多少CREATE TABLE order_item ( id INT(11) NOT NULL AUTO_INCREMENT, order_id INT(11) NOT NULL COMMENT 订单主表ID, product_id INT(11) NOT NULL, product_name VARCHAR(100) NOT NULL COMMENT 下单时的商品名快照, price DECIMAL(10,2) NOT NULL COMMENT 下单时的单价快照, quantity INT(11) NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;明细表冗余product_name和price两个快照字段很多人觉得违反规范化但这是电商实践里的标准做法商品改了名、改了价历史订单还要显示买家下单时的信息。如果不冗余订单详情页必须join商品表商品一旦删掉整条记录就断了。3. 把源码跑起来从环境准备到首次启动的完整路径3.1 版本搭配JDK、Maven、Tomcat、MySQL别在环境上浪费两天拿到一套SSM源码第一步不是急着看代码而是确认本机java环境配置和项目匹配。版本不匹配是新手翻车率最高的地方建议直接按下表配组件推荐版本说明JDK1.88u202及以上SSM最经典的运行版本兼容性最好Maven3.6.3依赖管理稳定和国内镜像源兼容Tomcat8.5.x支持Servlet 3.1和JDK8一起跑最稳MySQL5.7建表脚本兼容性最高5.1驱动直连IDEAIntelliJ IDEA Community免费版够用按Maven项目导入JDK装完先java -version验证Maven用mvn -v验证两条命令都通过再往下走。常见的java不是内部或外部命令基本就是JAVA_HOME没配或者配完没重启终端MySQL装好后把root密码记清楚后面jdbc.properties里要用。3.2 导入项目后必须检查的四个配置文件不管源码是Git克隆还是压缩包解压导入IDEA后先检查四个文件别急着点运行。第一个是jdbc.properties数据库连接都在这jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/computer_sales?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456数据库名、用户名、密码必须改成自己本机的值。url里的useUnicodetrue和characterEncodingutf8是中文不乱码的前提少了这两项商品名称和收货地址大概率变问号。第二个是web.xml重点看DispatcherServlet和编码过滤器filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingCharacterEncodingFilter必须在DispatcherServlet映射之前注册url-pattern用/*覆盖所有请求POST和GET的中文参数才会被统一成UTF-8。第三个是spring-context.xml里的组件扫描路径第四个是MyBatis的mapperLocationscontext:component-scan base-packagecom.shop /base-package必须和项目实际的包名一致。如果源码包名是com.xxx.shop而你扫的是com.shop所有Service都注入不进去启动直接报NoSuchBeanDefinitionException。Mapper XML路径建议在spring-context.xml里显式声明property namemapperLocations valueclasspath:mapper/*.xml /确保mapper目录下的XML在编译后被扫描到否则Mapper接口和XML对不上运行时抛BindingException。3.3 建库建表与首次启动两条MySQL命令完成冒烟数据库脚本通常在源码里带一份.sql文件找到后执行mysql -u root -p database/computer_sales.sql执行完先确认表建好没有mysql -u root -p -e use computer_sales; show tables;这条命令把登录、选库、查看表列表一步做完是mysql数据库常用命令里很实用的一招。确认表齐全后再看是否有初始化数据用户表和商品表里至少应有几条测试记录否则登录页和首页都是空的会误以为程序出了问题。然后配置Tomcat运行。IDEA里进入Run → Edit Configurations加一个Tomcat Server LocalDeployment选项卡里点加号选war explodedContext路径建议保持和项目名一致比如/shop浏览器访问地址就是http://localhost:8080/shop/。点启动后看到Tomcat started on port 8080就算成功。如果启动卡住或报错直接看Console标签页第一个红字不要靠刷新页面猜日志写得很清楚。4. 核心模块代码走读商品分页、购物车与订单事务4.1 商品分页查询MyBatis动态SQL与PageHelper插件商城首页和分类页的核心请求就是分页查询。没用过分页插件的人会自己写LIMIT加手动查count代码又长又容易算错。这套系统常用PageHelper插件用法和手动SQL互补public PageInfoProduct queryPage(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); ListProduct list productMapper.selectProductList(new Product()); return new PageInfo(list); }PageHelper.startPage()必须在紧接的第一次Mapper查询之前调用中间不能插入其他查询否则分页会跑偏。PageInfo里封装了总记录数、总页数、当前页数据和页码列表前端JSP直接用page.list和page.total取值。提示PageHelper.startPage()后面的第一个查询会被自动拼上LIMIT所以这行一定要和紧随其后的Mapper查询写在一起中间别插任何数据库操作。商品列表查询在ProductMapper.xml里用动态SQL实现多条件过滤select idselectProductList resultTypecom.shop.entity.Product SELECT id, name, price, image, sales FROM product where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testcategoryId ! null AND category_id #{categoryId} /if AND status 1 /where ORDER BY id DESC /select标签会自动处理AND前缀第一个条件成立时标签会自动去掉前面的AND避免SQL语法错误。用CONCAT(%, #{name}, %)而不是%${name}%是防SQL注入的关键——${}是字符串拼接直接拼进SQL#{}走预编译占位符参数值永远只是值。搜索、分类筛选、上下架过滤在一条SQL里全处理掉了不需要为每个条件写独立Mapper方法。4.2 购物车Session方案与数据库方案的取舍购物车实现有两条路线。Session方案把购物车对象放在用户浏览器会话里优点是不用建表、未登录也能加购、访问快缺点是换浏览器或清Cookie就丢。数据库方案落到cart表可跨设备同步但用户未登录时得用临时标识关联数据复杂度高不少。对这套毕设级商城我选Session方案加购Controller这么写RequestMapping(/add) public String add(Integer productId, Integer quantity, HttpSession session) { ListCartItem cart (ListCartItem) session.getAttribute(cart); if (cart null) { cart new ArrayList(); session.setAttribute(cart, cart); } for (CartItem item : cart) { if (item.getProductId().equals(productId)) { item.setQuantity(item.getQuantity() quantity); return redirect:/cart/list; } } CartItem item new CartItem(); item.setProductId(productId); item.setQuantity(quantity); cart.add(item); return redirect:/cart/list; }这里有个细节重复加购同一商品时先遍历已有条目命中了就改数量而不是无脑往list里塞否则购物车会出现同一商品两行。session里存的是CartItem而不是Product购物车字段只需要商品ID、单价和数量单价在加购时从商品表读出来快照进去避免结算时实时查库。Session方案最怕的就是用户关闭浏览器后购物车清空。如果有补救需求改造成本也不高登录成功后把session里的cart逐条写入cart表下次登录再查回来。这段逻辑放在登录Service的Transactional方法里一次事务写完。4.3 订单提交事务注解与条件更新的正确顺序订单模块是这套系统里唯一必须上事务的地方提交订单至少涉及订单主表、订单明细表、商品库存三张表。任何一步失败前面已写的数据都得回滚否则出现扣了库存没有订单的脏数据。Transactional(rollbackFor Exception.class) public void createOrder(Order order, ListOrderItem items) { for (OrderItem item : items) { int rows productMapper.reduceStock(item.getProductId(), item.getQuantity()); if (rows 0) { throw new RuntimeException(商品库存不足 item.getProductName()); } } orderMapper.insert(order); for (OrderItem item : items) { item.setOrderId(order.getId()); orderItemMapper.insert(item); } }Transactional(rollbackFor Exception.class)表示方法内任何异常都回滚整个事务。rollbackFor必须写成Exception.class因为Spring默认只对运行时异常回滚漏写这个参数的话业务代码里抛出的受检异常不触发回滚库存照样被扣。扣库存别在Java层先查库存再判断——两个并发请求同时读到库存1都以为有货一起往下走就超卖了。要直接在SQL里做原子判断UPDATE product SET stock stock - #{quantity}, sales sales #{quantity} WHERE id #{productId} AND stock #{quantity}这条UPDATE的影响行数由reduceStock返回等于0说明条件不满足在Java层把返回值当成库存不足处理。SQL把查库存、扣库存、校验合并成一个操作MySQL的行锁保证同一时刻只有一个请求能改这行是防超卖最朴素的方案比synchronized锁Service实例可靠得多。插入顺序也注意明细表需要order.getId()当外键所以必须先insert主表拿到自增ID再插入明细。5. 避坑手册SSM商城启动和上线阶段最常见的七个坑写SSM项目最耗时间的不是写功能是排错。下面七条是我在不同项目里反复见到的按现象、原因、解决三步讲清楚。5.1 启动报ClassNotFoundException先查Maven依赖树再看Tomcat lib现象Tomcat启动到一半抛java.lang.ClassNotFoundException比如org.springframework.web.context.ContextLoaderListener页面打不开。原因Maven依赖没下载完整或者依赖scope写成了provided导致war包没带jar。也有可能是本机Tomcat lib目录里的旧jar和项目冲突。解决IDEA右侧Maven面板看依赖是否有红色下划线或命令行跑mvn dependency:tree拉出依赖树检查spring-webmvc版本是否冲突再用压缩软件打开war包确认WEB-INF/lib里jar齐全。5.2 商品名变问号三层编码必须一致现象后台录入机械键盘保存后列表页显示????。原因数据库连接URL没有characterEncodingutf8Tomcat连接器没设URIEncoding页面contentType没写UTF-8三层里只要一层编码不一致中文就崩。解决数据库URL加useUnicodetruecharacterEncodingutf8server.xml的Connector节点加URIEncodingUTF-8JSP页头设置pageEncodingUTF-8。三个地方一起改不要试图只改一处。5.3 MySQL 8连不上驱动类名和时区两个雷现象启动报Access denied for user或Communications link failure账号密码明明没错。原因本机装的是MySQL 8.0项目还在用com.mysql.jdbc.Driver和旧版5.1驱动。MySQL 8把驱动类改成了com.mysql.cj.jdbc.Driver且要求URL里带serverTimezone参数。解决换8.x驱动URL改成jdbc:mysql://localhost:3306/computer_sales?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8如果报Public Key Retrieval is not allowed再拼上allowPublicKeyRetrievaltrue。5.4 CSS和JS全部404DispatcherServlet把静态资源拦了现象页面HTML出来了但没有任何样式F12看.css和.js全是404。原因DispatcherServlet的url-pattern配成/把静态资源请求也当成Controller请求分发没有对应handler就404。解决spring-mvc.xml里放行静态资源两选一我习惯用第一个mvc:resources mapping/static/** location/static/ /或者mvc:default-servlet-handler /配置完确认HTML里引用的是/static/css/style.css这种以/开头的绝对路径而不是相对路径css/style.css否则页面在不同目录层级下会引用错位置。5.5 Mapper绑定异常XML位置和namespace各占一半锅现象启动抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)或者接口方法能进但查询结果全null。原因Mapper接口的namespace和接口全限定名不一致或者Mapper XML放在src/main/java目录下Maven默认只打包resourcesXML根本没进classpath。解决namespace写成com.shop.mapper.ProductMapper这样的接口全限定名方法id和接口方法名一一对应XML统一放src/main/resources/mapper目录再在spring-context.xml配mapperLocations为classpath:mapper/*.xml。5.6 并发下单把库存扣成负数Java判断不可靠现象两个人同时下单最后一件商品两个订单都成功库存变-1。原因Java层先select库存再判断再update两个请求之间存在时间窗口都读到库存1都认为可以扣。解决用第4章那条UPDATE语句把校验挪进SQL条件里靠影响行数判断是否超卖。这是成本最低的防超卖方案不需要引入分布式锁毕设和中小商城完全够用。5.7 java启动失败八成是端口被占现象IDEA点启动立刻报Port 8080 was already in use或者Tomcat闪退。原因上次运行没正常停止Tomcat进程还挂在后台常见于强制关IDEA或断电。解决先定位进程再结束netstat -ano | findstr :8080 taskkill /PID 进程号 /FMac和Linux用lsof -i:8080配合kill -9。如果项目换过端口记得Tomcat的server.xml和IDEA的HTTP port设置要一致两个端口对不上同样报错。6. 从毕设到面试谈资功能验证清单与三个低风险改造6.1 上线前按业务流走一遍的验证点系统能跑起来只是第一步。交付前我会按一条完整业务流做冒烟注册新用户 → 登录 → 浏览商品列表 → 按分类筛选 → 搜索关键词 → 加购 → 修改数量 → 提交订单 → 查看订单列表 → 后台登录 → 上架/下架商品 → 修改库存。每一步都要在界面上看到真实数据变化而不是只看接口返回成功。数据库层面用这条SQL核对订单和明细的关联SELECT o.id, o.total_amount, oi.product_name, oi.quantity FROM order o JOIN order_item oi ON oi.order_id o.id WHERE o.user_id 1;然后故意在createOrder方法里抛一个异常看订单和库存是不是一起回滚。这个实验在毕业答辩时现场讲出来比念PPT有说服力得多。6.2 三个值得做的改造Druid连接池、MyBatis-Plus、Redis会话时间有余量的话按性价比做三个改造正好都能写进毕业论文和面试话术。第一个是换Druid连接池并开启监控页。把数据源配置从c3p0换成Druid访问/druid/index.html就能看到实时连接数、SQL执行频率和慢SQL列表。演示一句我能看到哪条SQL慢技术含量立刻不一样。第二个是单表Mapper改用MyBatis-Plus。Mapper接口继承BaseMapper后商品和分类的增删改查不用写XML了。但多表关联和复杂查询建议保留手写SQL订单明细就是典型别为了少写代码丢掉SQL灵活度。第三个是用Redis接管Session。Session购物车最大的弱点是重启和集群部署时丢失用spring-data-redis把Session存进Redis重启后购物车还在。改造量大概一两天但Session会话共享这个概念在面试里非常能打。做完这套系统我最大的体会是它逼着我把Spring的Bean生命周期、MyBatis的SQL映射、MySQL的锁和事务串成了一根线而不是散落的面试题。我也曾因为漏配mapperLocations查了一整晚这种玄学排查很折磨人但查明白之后对classpath和Maven构建的理解直接上了一个台阶。把多余的测试数据清掉把README里的配置步骤补全就可以放心部署演示了。希望帮到你。本文还有配套的精品资源点击获取