☰
SSM框架电脑配件商城系统:Java Web核心考点全解析
2026/10/26 17:03:30 网站建设 项目流程

简介:一套基于SSM框架的电脑配件销售商城Java项目,面向毕业设计、课程实战及SSM入门学习,解决商城系统从后台管理到前台购物的完整业务闭环。压缩包共1344个文件、22.21MB,不止包含源码和数据库脚本,还包含前端页面与配置文档,主要类型为Java类(132)、JSP页面(156)、JavaScript脚本(359)、CSS样式(166)、图片素材(png/jpg/gif等)以及XML配置,可直接导入IDEA或Eclipse运行。目前已有133人学习下载,适合需要成套参考的开发者。项目采用Spring+SpringMVC+MyBatis+Maven+MySQL架构,后台管理涵盖用户、商品分类、商品上下架、库存调整、订单管理、轮播图与新闻资讯;前台则支持注册登录、商品推荐与详情、收藏、购物车、立即购买、评论、余额/积分充值、支付和个人中心。附带的数据库脚本和毕业论文,便于理解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负责数据库增删改查,属于半自动ORM,SQL由开发者自己写,框架只做参数映射和结果集映射——商城这类查询条件多、需要动态拼SQL的业务,用半自动比全自动好控制得多。

pom.xml里这组依赖我配过很多次,可以直接抄:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.27</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.13</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>

spring-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) { PageInfo<Product> page = productService.queryPage(pageNum, pageSize); model.addAttribute("page", page); return "product/list"; } }

@Controller把类注册为Spring的Bean,@RequestMapping("/product")定义类级别的URL前缀,方法映射/list,最终访问路径就是/product/list。方法返回的"product/list"是逻辑视图名,对应webapp下WEB-INF/views/product/list.jsp,前后缀在spring-mvc.xml里通过InternalResourceViewResolver配置。这条链路每个环节职责单一,出错好定位:页面不对看JSP,请求不对看Controller,数据不对看Mapper SQL。

视图解析器配置是B/S架构里特别容易忽略的一块,少了它返回字符串时直接404:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean>

prefix和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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

价格用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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

明细表冗余product_name和price两个快照字段,很多人觉得违反规范化,但这是电商实践里的标准做法:商品改了名、改了价,历史订单还要显示买家下单时的信息。如果不冗余,订单详情页必须join商品表,商品一旦删掉整条记录就断了。

3. 把源码跑起来:从环境准备到首次启动的完整路径

3.1 版本搭配:JDK、Maven、Tomcat、MySQL别在环境上浪费两天

拿到一套SSM源码,第一步不是急着看代码,而是确认本机java环境配置和项目匹配。版本不匹配是新手翻车率最高的地方,建议直接按下表配:

组件推荐版本说明
JDK1.8(8u202及以上)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.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/computer_sales?useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=123456

数据库名、用户名、密码必须改成自己本机的值。url里的useUnicode=true和characterEncoding=utf8是中文不乱码的前提,少了这两项,商品名称和收货地址大概率变问号。

第二个是web.xml,重点看DispatcherServlet和编码过滤器:

<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

CharacterEncodingFilter必须在DispatcherServlet映射之前注册,url-pattern用/*覆盖所有请求,POST和GET的中文参数才会被统一成UTF-8。

第三个是spring-context.xml里的组件扫描路径,第四个是MyBatis的mapperLocations:

<context:component-scan base-package="com.shop" />

base-package必须和项目实际的包名一致。如果源码包名是com.xxx.shop而你扫的是com.shop,所有Service都注入不进去,启动直接报NoSuchBeanDefinitionException。Mapper XML路径建议在spring-context.xml里显式声明:

<property name="mapperLocations" value="classpath: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 Local,Deployment选项卡里点加号选war exploded,Context路径建议保持和项目名一致,比如/shop,浏览器访问地址就是http://localhost:8080/shop/。点启动后看到Tomcat started on port 8080就算成功。如果启动卡住或报错,直接看Console标签页第一个红字,不要靠刷新页面猜,日志写得很清楚。

4. 核心模块代码走读:商品分页、购物车与订单事务

4.1 商品分页查询:MyBatis动态SQL与PageHelper插件

商城首页和分类页的核心请求就是分页查询。没用过分页插件的人会自己写LIMIT加手动查count,代码又长又容易算错。这套系统常用PageHelper插件,用法和手动SQL互补:

public PageInfo<Product> queryPage(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List<Product> 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 id="selectProductList" resultType="com.shop.entity.Product"> SELECT id, name, price, image, sales FROM product <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="categoryId != 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) { List<CartItem> cart = (List<CartItem>) 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, List<OrderItem> 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没有characterEncoding=utf8,Tomcat连接器没设URIEncoding,页面contentType没写UTF-8,三层里只要一层编码不一致,中文就崩。解决:数据库URL加useUnicode=true&characterEncoding=utf8;server.xml的Connector节点加URIEncoding="UTF-8";JSP页头设置pageEncoding="UTF-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?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8;如果报Public Key Retrieval is not allowed,再拼上allowPublicKeyRetrieval=true。

5.4 CSS和JS全部404:DispatcherServlet把静态资源拦了

现象:页面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默认只打包resources,XML根本没进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 进程号 /F

Mac和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里的配置步骤补全,就可以放心部署演示了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询