1. 项目概述:SSM框架下的手机商城管理系统
这个基于SSM框架的手机商城管理系统,本质上是一个完整的B2C电商解决方案。我去年帮某高校计算机系评审毕业设计时,就见过三个不同版本的类似实现。最核心的价值在于,它用最经典的JavaEE技术栈,实现了从商品展示、购物车到订单处理的完整电商链路。
典型的应用场景包括:
- 计算机专业学生的课程设计/毕业设计(90%的使用场景)
- 小型创业团队快速搭建原型系统
- 传统零售商的线上业务试水
技术选型上,SSM(Spring+SpringMVC+MyBatis)组合堪称JavaWeb开发的"黄金三件套"。Spring的IoC容器管理着整个应用的Bean生命周期,SpringMVC处理前端请求的路由和响应,MyBatis则负责与MySQL数据库的交互。这种分层架构让系统具备了良好的可维护性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计解析
2.1 技术栈选型考量
选择SSM框架而非SpringBoot是有意为之的教学设计。通过手动配置spring-context.xml、spring-mvc.xml和mybatis-config.xml,学生能更透彻地理解:
- DispatcherServlet如何初始化WebApplicationContext
- HandlerMapping如何将URL映射到Controller方法
- SqlSessionFactory如何通过DataSource连接池管理数据库连接
这种"显式配置"虽然比SpringBoot的自动配置繁琐,但对理解框架原理至关重要。我在指导课程设计时,会要求学生手写至少三个关键配置:
xml复制<!-- spring-mvc.xml片段 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
2.2 分层架构实现
系统严格遵循MVC模式:
- Model层:MyBatis的Mapper接口配合XML映射文件,例如:
java复制@Repository
public interface PhoneMapper {
@Select("SELECT * FROM phone WHERE price BETWEEN #{min} AND #{max}")
List<Phone> selectByPriceRange(@Param("min") BigDecimal min,
@Param("max") BigDecimal max);
}
- View层:采用JSP+EL+JSTL组合,配合Bootstrap前端框架
- Controller层:SpringMVC的@Controller处理请求,如:
java复制@Controller
@RequestMapping("/cart")
public class CartController {
@Autowired
private CartService cartService;
@PostMapping("/add")
public String addItem(@RequestParam Long phoneId,
HttpSession session) {
User user = (User) session.getAttribute("currentUser");
cartService.addItem(user.getId(), phoneId);
return "redirect:/cart/list";
}
}
3. 核心功能模块实现
3.1 商品管理子系统
商品模块的数据库设计值得特别关注。我建议采用"垂直分表"策略:
sql复制CREATE TABLE `phone` (
`id` bigint(20) PRIMARY KEY,
`name` varchar(100) NOT NULL,
`price` decimal(10,2) NOT NULL,
-- 其他基础字段
);
CREATE TABLE `phone_detail` (
`phone_id` bigint(20) PRIMARY KEY,
`description` text,
`specifications` json DEFAULT NULL,
FOREIGN KEY (`phone_id`) REFERENCES `phone` (`id`)
);
这种设计解决了商品基础信息与详情信息访问频率不同的问题。在实现商品搜索时,我们使用MyBatis的动态SQL:
xml复制<select id="search" resultType="Phone">
SELECT * FROM phone
<where>
<if test="keyword != null">
AND name LIKE CONCAT('%',#{keyword},'%')
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
<!-- 其他条件 -->
</where>
LIMIT #{offset}, #{pageSize}
</select>
3.2 购物车与订单系统
购物车实现有两种常见方案:
- Session方案:适合未登录用户临时存储
java复制@GetMapping("/cart")
public String viewCart(HttpSession session) {
Cart cart = (Cart) session.getAttribute("cart");
if (cart == null) {
cart = new Cart();
session.setAttribute("cart", cart);
}
return "cart/view";
}
- 数据库方案:已登录用户的持久化存储
sql复制CREATE TABLE `cart_item` (
`id` bigint(20) PRIMARY KEY,
`user_id` bigint(20) NOT NULL,
`phone_id` bigint(20) NOT NULL,
`quantity` int(11) NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`phone_id`) REFERENCES `phone` (`id`)
);
订单处理最关键的是一致性保证。我们采用Spring的声明式事务管理:
java复制@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(OrderDTO dto) {
// 1. 扣减库存
phoneMapper.reduceStock(dto.getPhoneId(), dto.getQuantity());
// 2. 生成订单
Order order = convertToOrder(dto);
orderMapper.insert(order);
// 3. 清空购物车
cartMapper.clear(dto.getUserId());
}
}
4. 开发环境搭建实战
4.1 基础环境配置
推荐使用Maven进行依赖管理,pom.xml关键依赖包括:
xml复制<dependencies>
<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- MyBatis整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
4.2 数据库配置技巧
在jdbc.properties中配置连接池参数时,要注意:
properties复制# 开发环境配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/phone_mall?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
# 连接池优化参数
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.maxWait=60000
重要提示:永远不要在配置文件中使用root账号!应该创建专用数据库用户并限制权限:
sql复制CREATE USER 'mall_user'@'%' IDENTIFIED BY 'ComplexPwd123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON phone_mall.* TO 'mall_user'@'%';
5. 典型问题排查指南
5.1 中文乱码解决方案
乱码问题通常出现在三个环节:
- 数据库连接:需要在JDBC URL中添加参数
code复制jdbc:mysql://...?useUnicode=true&characterEncoding=UTF-8 - Servlet过滤器:在web.xml中配置
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> </filter> - JSP页面:添加page指令
jsp复制<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
5.2 事务失效场景
通过多年调试经验,我总结出SSM中事务失效的五大原因:
- 方法访问权限不是public
- 方法被同类中其他方法调用(代理失效)
- 异常类型非RuntimeException且未声明@Transactional(rollbackFor=...)
- 数据库引擎不支持事务(如MyISAM)
- 多数据源未正确配置事务管理器
测试事务是否生效的小技巧:
java复制@Service
public class TestService {
@Transactional
public void testTransaction() {
try {
// 执行数据库操作
throw new RuntimeException("Rollback test");
} catch (Exception e) {
System.out.println("如果看到这条消息但数据没回滚,说明事务没生效");
}
}
}
6. 项目优化方向建议
6.1 性能优化实践
对于商品列表页的缓存实现,可以采用二级缓存策略:
- 本地缓存:使用Caffeine缓存热点商品
java复制@Configuration
public class CacheConfig {
@Bean
public Cache<Long, Phone> phoneCache() {
return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
}
}
- 分布式缓存:集成Redis缓存分类查询结果
java复制@Cacheable(value = "phoneCategory", key = "#categoryId")
public List<Phone> getByCategory(Long categoryId) {
return phoneMapper.selectByCategory(categoryId);
}
6.2 安全加固方案
我强烈建议至少实现以下安全措施:
- 密码加密:使用BCryptPasswordEncoder替代MD5
java复制@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- XSS防护:自定义HtmlEscape编辑器
java复制@ControllerAdvice
public class XssProtectionAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class,
new StringEscapeEditor(true, false));
}
}
- CSRF防护:在Spring Security配置中启用
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
7. 文档编写与答辩准备
7.1 数据库设计文档
ER图绘制建议使用PowerDesigner或Navicat逆向生成,但需要手动补充设计说明:
code复制表名:order_detail
字段说明:
- payment_type:1-支付宝 2-微信 3-银联
- status:0-未支付 1-已支付 2-已发货 3-已完成 4-已取消
索引设计:
- 联合索引(user_id, create_time)用于用户订单查询
- 单列索引(order_no)用于订单号精确查询
7.2 答辩常见问题准备
根据我参与的30+次答辩评审,高频问题包括:
- 如何解决高并发下的库存超卖?
- 答案:乐观锁(version字段)+ Redis原子操作
- 为什么选择SSM而不是SpringBoot?
- 答案:更深入理解框架原理,适合教学场景
- 系统的扩展性体现在哪些方面?
- 答案:分层架构、接口抽象、可插拔模块设计
在实现支付模块的模拟时,可以采用状态机模式:
java复制public enum OrderStatus {
UNPAID {
@Override
public boolean canChangeTo(OrderStatus newStatus) {
return newStatus == PAID || newStatus == CANCELLED;
}
},
PAID {
// 其他状态转换逻辑
};
public abstract boolean canChangeTo(OrderStatus newStatus);
}
8. 源码结构与开发规范
8.1 项目目录规划
标准的Maven项目结构应包含:
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── phonemall/
│ │ ├── config/ # Spring配置类
│ │ ├── controller/ # MVC控制器
│ │ ├── service/ # 业务逻辑
│ │ ├── dao/ # MyBatis接口
│ │ └── model/ # 实体类
│ ├── resources/
│ │ ├── mapper/ # MyBatis XML
│ │ ├── spring/ # Spring配置文件
│ │ └── application.properties
│ └── webapp/
│ ├── WEB-INF/
│ │ └── views/ # JSP文件
│ └── resources/ # 静态资源
└── test/ # 测试代码
8.2 编码规范要点
根据阿里巴巴Java开发手册,特别需要注意:
- 类名使用UpperCamelCase,如PhoneController
- 方法名使用lowerCamelCase,如getPhoneById
- MyBatis的Mapper接口名与XML文件名严格一致
- 所有Service方法必须添加javadoc注释:
java复制/**
* 根据条件分页查询手机列表
* @param query 包含priceRange, keyword等查询条件
* @param pageNum 当前页码
* @param pageSize 每页条数
* @return 分页结果,包含列表和总数
*/
PageResult<Phone> queryPhones(PhoneQuery query, int pageNum, int pageSize);
在项目打包部署时,推荐使用maven-war-plugin配置:
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>phonemall</warName>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.jsp</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
