1. 项目概述与设计思路
这个基于SSM框架的水果蔬菜商城系统,是我去年带队开发的一个实战项目。当时我们团队接到一个本地有机农场主的委托,希望建立一个能够直接面向消费者的在线销售平台。经过需求分析和技术选型,最终决定采用Spring+SpringMVC+MyBatis这套经典组合。
选择SSM框架主要基于三点考虑:首先,Spring的IoC和AOP特性能够很好地管理业务组件和事务;其次,MyBatis相比Hibernate在复杂SQL查询方面更灵活;最后,这套技术栈在国内Java开发者中普及度高,后期维护成本低。实际开发中我们发现,这种架构确实在业务逻辑复杂度和开发效率之间取得了很好的平衡。
系统采用标准的三层架构设计:
- 表现层:JSP+JSTL+EL表达式
- 业务逻辑层:Spring管理的Service组件
- 数据访问层:MyBatis实现的Mapper接口
特别提醒:在搭建项目骨架时,建议使用Maven的archetype插件生成标准目录结构,这能避免后续很多配置路径问题。我们最初手动创建目录就遇到了资源文件加载失败的坑。
2. 核心功能模块实现
2.1 用户认证模块
用户模块采用经典的邮箱+密码注册方式,密码存储使用Spring Security的BCryptPasswordEncoder进行加密。这里有个细节值得分享:我们最初使用MD5加密,后来在安全审计时发现强度不够,迁移到BCrypt花了不小功夫。
关键代码示例:
java复制@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public String register(@Valid User user, BindingResult result) {
if(result.hasErrors()){
return "register";
}
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.save(user);
return "redirect:/login";
}
}
2.2 商品展示模块
商品分类我们设计了三级结构(大类->小类->具体商品),使用MySQL的递归查询实现。前端采用懒加载技术,只有当用户展开某个分类时才请求子分类数据,这显著降低了首屏加载时间。
商品搜索功能整合了Elasticsearch实现全文检索,支持按价格区间、产地、有机认证等多维度筛选。这里有个性能优化点:对于高频访问的热门商品,我们使用Redis做了缓存,QPS提升了3倍左右。
2.3 购物车与订单系统
购物车设计采用了混合存储策略:已登录用户的数据持久化到数据库,未登录用户使用Cookie存储。两者在用户登录时会自动合并,这个功能虽然简单但极大提升了用户体验。
订单状态机是我们花时间最多的部分之一,完整的状态流转包括:
- 待支付(15分钟超时自动取消)
- 已支付待发货
- 已发货
- 已完成
- 已取消
- 已退款
每个状态变更都通过Spring的事件机制发送通知,并记录操作日志以便后续审计。
3. 后台管理系统关键实现
3.1 商品管理
后台采用EasyUI框架快速搭建管理界面,支持Excel批量导入商品信息。这里有个实用技巧:我们开发了一个图片自动压缩服务,上传的商品图片会自动生成适合列表页、详情页的不同尺寸版本。
商品上下架使用了MySQL的事务特性确保数据一致性:
java复制@Transactional
public void updateProductStatus(Long id, boolean online) {
Product product = productMapper.selectById(id);
if(product.getStock() <= 0 && online){
throw new RuntimeException("库存不足不能上架");
}
product.setOnline(online);
productMapper.updateById(product);
}
3.2 订单处理流程
管理员后台集成了快递鸟API实现物流跟踪,打印面单时使用了开源的JasperReport生成PDF。在处理退款申请时,我们接入了支付宝的退款接口,这里要特别注意幂等性处理,避免重复退款。
4. 系统部署与性能优化
4.1 生产环境配置
我们使用Nginx+Tomcat集群部署,Nginx负责静态资源和负载均衡,Tomcat实例通过Redis实现Session共享。数据库采用主从架构,读写分离通过MyBatis的插件实现。
关键配置示例(application-prod.properties):
code复制spring.datasource.druid.initial-size=5
spring.datasource.druid.max-active=20
spring.datasource.druid.validation-query=SELECT 1
spring.redis.timeout=3000
4.2 性能调优经验
- JVM参数调优:新生代与老年代比例设置为1:2,避免频繁Full GC
- MyBatis二级缓存:对商品分类等变化少的数据启用
- 数据库索引优化:为订单表的user_id和create_time建立复合索引
- 静态资源CDN加速:将图片等资源托管到阿里云OSS
压力测试时发现,商品详情页的数据库查询是瓶颈所在。我们通过以下方案解决:
- 添加Redis缓存层
- 优化SQL语句,避免SELECT *
- 对商品描述等大字段使用垂直分表
5. 开发中的典型问题与解决方案
5.1 事务失效问题
我们曾遇到@Transactional注解不生效的情况,排查发现是因为:
- 方法被同类中其他方法调用(代理失效)
- 异常类型不是RuntimeException
- 数据库引擎不支持事务(MyISAM)
解决方案:
- 将事务方法移到单独Service
- 明确指定rollbackFor
- 确保使用InnoDB引擎
5.2 并发修改问题
秒杀场景下出现过超卖问题,最终通过三种方案解决:
- 乐观锁:version字段控制
- Redis分布式锁
- 数据库行级锁(select for update)
实际测试发现,方案2的性能最好,核心代码如下:
java复制public boolean seckill(Long productId) {
String lockKey = "lock:" + productId;
try {
// 获取分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if(locked != null && locked){
// 执行库存扣减
return reduceStock(productId);
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
5.3 跨域问题
前端分离部署时遇到跨域问题,解决方案:
- 添加CORS过滤器
- Nginx配置反向代理
- 使用@CrossOrigin注解
我们最终选择了方案1,因为可以统一处理所有请求:
java复制@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new FilterRegistrationBean<>(new CorsFilter(source));
}
6. 项目扩展与改进方向
目前系统已经稳定运行了一年多,根据实际运营情况,我认为还可以在以下方面进行优化:
- 推荐系统:基于用户行为数据实现个性化推荐
- 会员体系:引入积分和等级制度
- 供应链对接:与农场ERP系统集成
- 微信小程序端:提升移动端体验
特别在缓存策略上,我们计划引入多级缓存架构:
- 第一层:本地缓存(Caffeine)
- 第二层:Redis集群
- 第三层:MySQL
这个项目给我的最大启示是:电商系统的核心不在于技术有多先进,而在于对业务场景的深入理解。比如生鲜商品需要特别关注库存时效性,我们的解决方案是每天凌晨自动下架零库存商品,并设置不同的库存预警阈值。