简介:这是一套完整的Java Web网上商城实战项目源码,面向Java初学者与SSM框架学习者,帮助掌握企业级电商系统从前台展示到后台管理的全链路开发能力。资源包含1491个文件,涵盖75个核心Java业务类、40个JSP页面(含商品列表、购物车、订单结算等前台模块)、94个XML配置文件(Spring/MyBatis核心配置)、1133张界面截图与图标素材(jpg/png),以及CSS、JS、SQL等配套资源,整体压缩包达181.11MB,结构清晰、模块划分明确。已有877人学习下载,项目已预置完整数据库脚本与多套前端样式(如fore_home.css、admin_main.css、layui集成样式),支持JDK1.8+Tomcat8.5+MySQL快速部署。读者可直接运行并深入理解用户认证、商品分类管理、购物车持久化、订单状态流转、收货地址维护等典型电商功能实现逻辑,同时获得SSM整合、Maven依赖管理及前后端交互(Ajax+JQuery)的一线实践范例。
1. 这不是“又一个SSM商城”,而是Java Web工程能力的完整切片
网上商城项目(Java+SSM+JSP+MySQL+Maven)在2024年仍被大量高校课程设计、初级Java岗笔试题、中小型外包交付反复选用,不是因为它多先进,而是它恰好卡在「能覆盖Java Web全链路核心能力」的黄金交点上:Maven管理依赖与生命周期、Spring IoC/AOP解耦业务、SpringMVC处理HTTP路由与参数绑定、MyBatis完成ORM映射与SQL控制、JSP承担服务端模板渲染、MySQL提供事务性数据存储。它不追求高并发或微服务架构,但要求开发者亲手配置Tomcat、调试JSP编译路径、手写Mapper XML、排查MyBatis一级缓存失效、理解Maven依赖传递冲突——这些恰恰是Java面试中高频追问的底层细节。适合刚学完Servlet/JDBC、正从单体Web向框架过渡的开发者,也适合面试前用真实模块验证自己对SSM各层协作的理解是否停留在“能跑通”层面。
2. 搭建可运行的最小SSM商城骨架:从Maven依赖到SpringMVC DispatcherServlet注册
2.1 Maven pom.xml中SSM核心依赖的版本协同逻辑
SSM组合不是简单堆砌最新版,必须考虑Spring 5.x与MyBatis 3.4+、JSP 2.3与Servlet 4.0的兼容边界。当前稳定组合采用:Spring 5.3.32(兼容JDK 8/11)、MyBatis 3.4.6(避免3.5+对JDK 17的强依赖)、mybatis-spring 1.3.2(桥接Spring事务)、javax.servlet-api 4.0.1(provided scope)、jsp-api 2.3.3(provided)。关键点在于spring-webmvc必须显式声明,否则DispatcherServlet无法加载;jstl需引入1.2版本,新版JSTL 1.3在Tomcat 9+中存在EL表达式解析差异。
<dependencies> <!-- Spring Core & MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.32</version> </dependency> <!-- MyBatis & Spring整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <!-- JSP支持 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> </dependencies>提示:
mysql-connector-java8.0.33需配合MySQL 5.7+使用,若连接老版本MySQL 5.6,必须降级至5.1.47,否则报Unknown system variable 'query_cache_size'错误。Maven会自动下载jar包到本地仓库(默认~/.m2/repository),可通过mvn dependency:tree -Dverbose查看依赖树,确认无重复或冲突版本。
2.2 web.xml中DispatcherServlet的注册与初始化参数
JSP项目必须保留web.xml(Servlet 3.0+注解方式在JSP场景下易导致视图解析器失效),核心是正确注册DispatcherServlet并指定contextConfigLocation指向Spring配置文件。注意load-on-startup设为1确保优先加载,<url-pattern>/</url-pattern>覆盖所有请求,但需配合<init-param>关闭静态资源拦截。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Spring容器初始化 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- SpringMVC前端控制器 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- JSP编译配置(关键!) --> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <page-encoding>UTF-8</page-encoding> <scripting-invalid>false</scripting-invalid> </jsp-property-group> </jsp-config> </web-app>2.2.1 Spring配置文件拆分策略:为什么必须分离spring-context.xml和spring-mvc.xml
spring-context.xml负责IoC容器基础:数据源、事务管理器、MyBatis SqlSessionFactory、Service层Bean;spring-mvc.xml专注Web层:HandlerMapping、ViewResolver、MultipartResolver、拦截器。若合并为单文件,会导致Controller中@Autowired Service失败——因为DispatcherServlet加载的上下文是子容器,无法访问父容器(ContextLoaderListener加载)中的Bean。典型错误日志:No qualifying bean of type 'xxx.service.UserService' available。
2.3 JSP页面与SpringMVC视图解析器的路径映射规则
JSP文件必须放在/WEB-INF/jsp/目录下(安全强制),ViewResolver需配置前缀后缀。常见错误是InternalResourceViewResolver的prefix漏掉/WEB-INF/或suffix写成.jsp.(多一个点)。以下配置使return "user/list"实际跳转到/WEB-INF/jsp/user/list.jsp:
<!-- spring-mvc.xml --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="order" value="1"/> </bean>注意:Tomcat启动时会在
work/Catalina/localhost/[appname]/org/apache/jsp/生成JSP编译后的Java类和class文件。若修改JSP后页面未更新,先清空该目录再重启Tomcat——这是JSP热部署失效的最常见原因,而非代码问题。
3. 实现商品管理模块:从MySQL建表到MyBatis动态SQL分页查询
3.1 MySQL商品表设计与字符集陷阱
商城核心表product需支持中文搜索与价格精度,字段设计必须规避常见坑:name用VARCHAR(100)而非TEXT(影响索引效率);price用DECIMAL(10,2)而非FLOAT(避免0.1+0.2≠0.3);create_time用DATETIME而非TIMESTAMP(避免时区转换异常)。建表语句必须显式指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,否则微信昵称等emoji插入失败。
CREATE TABLE `product` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL COMMENT '商品名称', `price` DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '价格', `stock` INT NOT NULL DEFAULT '0' COMMENT '库存', `status` TINYINT NOT NULL DEFAULT '1' COMMENT '状态:1上架,0下架', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;提示:MySQL 8.0默认字符集已是utf8mb4,但旧版本需在
my.cnf中添加[client] default-character-set = utf8mb4和[mysqld] character-set-server = utf8mb4,否则JDBC连接URL中的useUnicode=true&characterEncoding=utf8无效。
3.2 MyBatis Mapper接口与XML的动态SQL实现
商品列表需支持按名称模糊搜索、按状态筛选、分页,MyBatis<where>标签自动处理SQL拼接空格与AND。<if>嵌套避免NPE,<bind>预编译LIKE参数防止SQL注入。分页使用RowBounds(内存分页)仅适用于小数据量,生产环境必须改用物理分页插件(如PageHelper),但本项目为教学目的保留原生写法。
<!-- ProductMapper.xml --> <select id="selectList" resultType="com.example.entity.Product"> SELECT * FROM product <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="status != null"> AND status = #{status} </if> </where> ORDER BY create_time DESC </select>// ProductMapper.java public interface ProductMapper { List<Product> selectList(@Param("name") String name, @Param("status") Integer status); // 分页查询:传入RowBounds对象 List<Product> selectListWithRowBounds(@Param("name") String name, @Param("status") Integer status, RowBounds rowBounds); }3.2.1 Service层事务控制与MyBatis SqlSession生命周期
@Transactional必须加在Service方法上(非Controller),且事务管理器<tx:annotation-driven/>需在spring-context.xml中声明。关键点:MyBatis的SqlSession由SqlSessionFactory创建,Spring通过SqlSessionTemplate代理保证线程内唯一性。若在Service中手动调用sqlSession.close(),将导致后续DAO操作抛出SqlSession is closed异常。
<!-- spring-context.xml --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductMapper productMapper; @Override @Transactional(rollbackFor = Exception.class) public void updateStock(Long productId, Integer change) { Product product = productMapper.selectById(productId); if (product.getStock() + change < 0) { throw new RuntimeException("库存不足"); } productMapper.updateStock(productId, change); // 更新库存 // 若此处抛异常,updateStock操作自动回滚 } }4. JSP页面开发与常见陷阱:EL表达式失效、表单提交乱码、JSON数据交互
4.1 JSP页面编码与Tomcat请求参数乱码解决方案
JSP页面顶部必须声明<%@ page contentType="text/html;charset=UTF-8" %>,但仅此不够。Tomcat 8+默认使用UTF-8解码GET请求,但POST请求仍需CharacterEncodingFilter过滤器。在web.xml中配置该过滤器,且<url-pattern>/*</url-pattern>必须置于所有servlet mapping之前。
<!-- web.xml --> <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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>提示:若未配置此过滤器,JSP表单提交中文到Controller时,
@RequestParam值显示为????。此时检查Tomcat日志中是否有CharacterEncodingFilter初始化日志,确认过滤器已生效。
4.2 JSP中EL表达式与JSTL标签的正确使用范式
EL表达式${product.name}失效的三大原因:web.xml中<web-app>版本低于2.4(需3.0+)、JSP页面未声明isELIgnored="false"(默认true)、JSTL jar未正确引入。标准写法如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>商品列表</title></head> <body> <c:forEach items="${productList}" var="product"> <div> 名称:${product.name} | 价格:¥${product.price} <a href="product/detail?id=${product.id}">查看详情</a> </div> </c:forEach> </body> </html>4.2.1 JSP向JavaScript传递Java对象的JSON化技巧
直接<script>var data = ${jsonString};</script>存在XSS风险且JSON格式易错。正确做法是使用JSTL<c:out>转义,或在Controller中将对象转为JSON字符串(用Jackson)后放入request域:
// Controller @RequestMapping("/product/list") public String list(HttpServletRequest request) { List<Product> list = productService.selectList(null, null); ObjectMapper mapper = new ObjectMapper(); try { String json = mapper.writeValueAsString(list); request.setAttribute("productJson", json); } catch (JsonProcessingException e) { e.printStackTrace(); } return "product/list"; }<script> const productList = JSON.parse('<c:out value="${productJson}" escapeXml="true"/>'); console.log(productList[0].name); // 安全获取 </script>5. Maven构建与Tomcat部署的调试技巧:定位JSP编译失败、MyBatis SQL日志、MySQL连接超时
5.1 快速定位JSP编译失败的三步法
当访问JSP页面报org.apache.jasper.JasperException: Unable to compile class for JSP,按顺序检查:
- 确认JSP语法:检查
<%=与%>是否配对,EL表达式${}内是否引用了null对象; - 查看Tomcat work目录:进入
work/Catalina/localhost/[appname]/org/apache/jsp/,找到对应JSP生成的.java文件,用IDE打开查看编译错误行; - 验证依赖完整性:执行
mvn clean compile,确认target/classes下存在spring-context.xml等配置文件,target/[appname]/WEB-INF/lib/包含jstl-1.2.jar和standard-1.1.2.jar(JSTL 1.2需配套standard库)。
5.2 开启MyBatis SQL日志与MySQL慢查询分析
MyBatis日志输出需在log4j2.xml中配置org.apache.ibatis为DEBUG级别,并确保pom.xml引入log4j-core和log4j-api。MySQL慢查询需在my.cnf中开启:
# my.cnf slow_query_log = ON slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 1 log_queries_not_using_indexes = ON然后在Spring配置中启用MyBatis日志:
<!-- log4j2.xml --> <Logger name="org.apache.ibatis" level="debug" additivity="false"> <AppenderRef ref="Console"/> </Logger>提示:若MyBatis日志不输出,检查
resources目录下log4j2.xml是否被Maven正确复制到target/classes,执行mvn resources:resources可强制刷新。
5.3 MySQL连接超时与连接池配置优化
默认MySQL连接8小时超时,导致长时间空闲后首次请求报Communications link failure。解决方案有二:
- 应用层:在JDBC URL中添加
autoReconnect=true&failOverReadOnly=false&maxReconnects=10(MySQL 5.1+已废弃,不推荐); - 连接池层(推荐):使用Druid连接池,在
spring-context.xml中配置testWhileIdle和timeBetweenEvictionRunsMillis:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/mall?useSSL=false&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> <property name="initialSize" value="5"/> <property name="minIdle" value="5"/> <property name="maxActive" value="20"/> <property name="testWhileIdle" value="true"/> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="validationQuery" value="SELECT 1"/> </bean>此配置使连接池每60秒检测空闲连接有效性,发现失效连接自动剔除并重建,彻底规避超时问题。
本文还有配套的精品资源,点击获取