简介:本资源是一套面向计算机专业本科生毕业设计及Java初学者实战训练的客户关系管理(CRM)系统,基于SSM(Spring+SpringMVC+MyBatis)框架开发,覆盖客户资料管理、服务跟踪、交易记录、客户等级与开发进度等核心业务场景。压缩包共3个文件(1个主程序ZIP、1个SQL数据库脚本、1个项目说明TXT),总大小25.18MB,其中SQL脚本可一键初始化MySQL数据结构,TXT文档详述环境配置与运行步骤,主ZIP含完整Eclipse工程源码,支持JDK+Tomcat快速部署。已有1111人学习下载,系统采用分层架构与角色权限控制,内置管理员、客户经理、营销主管三类用户,功能模块清晰、界面简洁、调试完备,可直接用于毕设答辩或Java Web开发能力强化训练。
1. 项目概述:一个典型的SSM毕设项目能教会你什么?
又到了一年一度的毕业季,对于计算机相关专业的同学来说,毕业设计是绕不过去的一道坎。很多同学一听到“毕设”两个字就头疼,感觉无从下手,特别是那些平时实践机会不多的同学。如果你正在为“基于SSM的客户资料管理系统”这类题目发愁,或者想找一个结构清晰、能跑通、还能学到东西的项目来练手,那这篇文章就是为你准备的。
这个项目,说白了,就是一个用Java技术栈实现的、用来管理客户信息的Web系统。它麻雀虽小,五脏俱全,涵盖了企业级Java开发中最经典、最实用的技术组合:SSM框架(Spring + Spring MVC + MyBatis)。为什么说它适合毕设和初学者?因为它需求明确(增删改查客户信息),技术栈成熟稳定,网上资料丰富,而且从数据库设计到前端展示,完整地走一遍流程,能让你对Web开发有一个系统性的认识。你做完它,不仅能交出一份像样的毕业设计,更重要的是,你能真正理解一个Web应用是如何从零到一搭建起来的,这对你后续找工作、做项目有莫大的帮助。
2. 项目整体设计与技术选型思路
2.1 为什么是SSM框架?
在开始动手之前,我们先聊聊为什么选择SSM作为技术栈,而不是更新的Spring Boot。这对于理解项目架构至关重要。
SSM是Spring、Spring MVC和MyBatis三个框架的集成。Spring Boot的出现确实简化了配置,但对于学习而言,SSM能让你更清晰地看到各个组件的职责和它们是如何协作的。Spring Boot帮你把轮子装好了,而SSM需要你自己动手组装轮子,这个过程能加深你对框架原理的理解。
- Spring:它是整个项目的“大管家”,负责管理所有对象(Bean)的生命周期和依赖关系。比如,我们的Service层需要调用Dao层,这个“调用关系”就是由Spring通过依赖注入(DI)来完成的,而不是我们在代码里
new出来的。这样做的好处是解耦,方便测试和维护。 - Spring MVC:它负责处理Web请求。当用户在浏览器点击一个按钮,发起一个“添加客户”的请求时,这个请求会先到达Spring MVC的前端控制器(DispatcherServlet),然后由它找到对应的控制器(Controller)方法来处理。Controller处理完业务逻辑(通常调用Service),最后返回一个视图(比如JSP页面)或者数据(JSON)给浏览器。它是连接用户操作和后端逻辑的桥梁。
- MyBatis:它是一个优秀的持久层框架,负责和数据库打交道。我们写SQL语句,MyBatis帮我们执行,并把数据库返回的结果集,自动映射成我们定义好的Java对象。相比于Hibernate的全自动ORM,MyBatis半自动的方式让我们对SQL有完全的控制力,这对于性能优化和复杂查询来说非常有利,也更符合很多国内开发者的习惯。
注意:对于毕设项目,使用SSM而非Spring Boot,在答辩时可能更能体现你对基础框架的理解。你可以清晰地阐述web.xml、Spring配置文件、MyBatis配置文件各自的作用,这比直接说“Spring Boot自动配置”更有技术深度。
2.2 系统核心功能模块拆解
一个客户资料管理系统,核心功能无非是对客户信息的“增删改查”(CRUD)。但一个好的设计,会让这些操作更安全、更高效、更易用。我们通常会将系统划分为以下几个层次:
- 表现层(View):用户直接操作的界面。这里我们选择经典的JSP + JSTL + Bootstrap的组合。JSP用于动态生成页面,Bootstrap能让我们的界面快速变得美观且响应式,适配电脑和手机。为什么不直接用HTML?因为我们需要在页面上展示从数据库查出来的动态数据,JSP可以嵌入Java代码片段(虽然不推荐大量使用)或使用JSTL标签库来循环展示客户列表。
- 控制层(Controller):接收页面请求,调用业务逻辑,决定返回哪个页面或数据。这是Spring MVC的舞台。每个功能,比如“查询客户列表”、“新增客户”,都会对应Controller中的一个方法。
- 业务逻辑层(Service):处理具体的业务规则。比如,在新增客户前,需要检查手机号格式是否合法、邮箱是否重复等。这一层是核心业务逻辑所在,它调用持久层的方法,但封装了更复杂的业务判断。
- 持久层(Dao/Mapper):直接操作数据库,执行SQL语句。这一层接口和SQL映射文件(XML)由MyBatis管理。
- 实体层(Entity/Model):就是普通的Java类(POJO),用来对应数据库中的一张表。例如
Customer类,里面有id、name、phone、email等属性,和数据库t_customer表的字段一一对应。
这种分层架构的好处是职责清晰,任何一层的变化,只要接口不变,对其他层的影响就最小。比如,今天用MySQL,明天想换Oracle,你只需要修改MyBatis的SQL映射和数据库连接配置,Service层以上的代码几乎不用动。
2.3 数据库设计要点
数据库设计是项目的基石,设计得好,后期开发事半功倍。对于客户表,我们至少需要以下字段:
CREATE TABLE `t_customer` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '客户ID(主键)', `name` varchar(50) NOT NULL COMMENT '客户姓名', `gender` tinyint(1) DEFAULT NULL COMMENT '性别:0-女,1-男', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `email` varchar(100) DEFAULT NULL COMMENT '电子邮箱', `company` varchar(200) DEFAULT NULL COMMENT '公司名称', `position` varchar(100) DEFAULT NULL COMMENT '职位', `address` varchar(500) DEFAULT NULL COMMENT '地址', `remark` text COMMENT '备注', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`), -- 手机号唯一索引 KEY `idx_name` (`name`) -- 姓名普通索引,方便按姓名搜索 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户信息表';设计思考:
id:主键,自增。这是表的唯一标识。gender:用tinyint存储,比varchar存储“男”、“女”更节省空间。在代码中用枚举类来映射。phone:设为唯一约束,因为手机号通常是客户的唯一标识。这里有个坑:如果业务允许客户没有手机号,唯一约束可能会因为多个NULL值而失效(具体看数据库版本),需要根据实际情况处理。create_time和update_time:这两个时间戳字段强烈建议加上。它们对于数据追溯、审计和分析非常有价值。ON UPDATE CURRENT_TIMESTAMP可以让记录在更新时自动刷新update_time。utf8mb4字符集:支持存储Emoji等特殊字符,避免出现乱码问题。- 索引:在
phone和name上建索引。phone是唯一索引,保证唯一性并加速基于手机号的查询。name是普通索引,加速按姓名模糊搜索(LIKE ‘%张%’)。但要注意,索引不是越多越好,会影响插入和更新速度。
3. 开发环境搭建与项目初始化
3.1 工具选型与配置
工欲善其事,必先利其器。一套顺手的开发环境能极大提升效率。
- JDK:选择JDK 8或JDK 11。JDK 8依然是目前企业中使用最广泛的LTS版本,兼容性最好。建议从Oracle官网或AdoptOpenJDK下载。
- IDE:IntelliJ IDEA Ultimate(学生可以免费申请许可)是首选。它对Java和Spring系列框架的支持是最好的,智能提示、代码生成、调试功能都非常强大。Eclipse也可以,但效率和体验上IDEA更优。
- 项目管理与构建工具:Maven。它帮你管理项目依赖(Jar包),你只需要在
pom.xml文件中声明需要什么框架、什么版本,Maven会自动从中央仓库下载,并解决包之间的依赖关系。再也不用手动下载一堆Jar包了。 - 数据库:MySQL 5.7或8.0。安装时记住root密码。同时,需要一个图形化的数据库管理工具,Navicat或DBeaver(免费)都可以,方便你执行SQL脚本、查看数据。
- 应用服务器:本地开发使用Tomcat 8.5或9。IDEA可以很方便地集成Tomcat,进行热部署调试。
实操心得:在IDEA中配置Maven时,建议使用自己下载的Maven,并修改其
conf/settings.xml文件,配置阿里云的镜像仓库,这样下载依赖包的速度会飞起。同时,将本地仓库路径改到非系统盘,避免C盘爆满。
3.2 创建Maven Web项目与SSM整合
这是项目的基础骨架搭建,步骤虽多,但每一步都有其意义。
- 创建项目:在IDEA中,选择新建Maven项目,选择
maven-archetype-webapp原型。这会生成一个标准的Web项目结构。 - 完善目录结构:Maven原型生成的结构不完整,需要手动补全。这是标准的SSM项目目录:
src/main/java com.yourcompany.crm.controller // 控制层 com.yourcompany.crm.service // 业务层接口 com.yourcompany.crm.service.impl // 业务层实现 com.yourcompany.crm.dao // 数据层接口(MyBatis Mapper接口) com.yourcompany.crm.entity // 实体类 com.yourcompany.crm.config // 配置类(可选,如果用Java Config) src/main/resources /spring // Spring配置文件 applicationContext.xml spring-mvc.xml /mybatis // MyBatis配置文件 mybatis-config.xml /mappers // MyBatis的SQL映射XML文件 CustomerMapper.xml jdbc.properties // 数据库连接属性文件 log4j2.xml // 日志配置文件 src/main/webapp /WEB-INF web.xml // Web部署描述符 /static /css /js /img /WEB-INF/jsp // JSP页面存放目录 - 编写pom.xml:这是项目的“食材清单”。你需要声明依赖,核心依赖包括:
- Spring Core:
spring-context,spring-beans,spring-core - Spring MVC:
spring-webmvc - MyBatis:
mybatis - MyBatis-Spring整合:
mybatis-spring(关键!) - 数据库驱动:
mysql-connector-java - 数据库连接池:推荐使用
com.alibaba.druid,它功能强大,自带监控。 - JSP相关:
javax.servlet-api,javax.servlet.jsp-api,jstl - 日志:
log4j-core,log4j-api(Log4j2) 或slf4j+logback - JSON处理:
jackson-databind(用于Controller返回JSON数据给前端) - 测试:
junit
- Spring Core:
- 配置web.xml:这是Web应用的入口。你需要在这里配置:
- Spring的监听器:
ContextLoaderListener,用于加载Spring的根容器(主要管理Service、Dao等Bean)。 - Spring MVC的前端控制器:
DispatcherServlet,用于拦截所有请求,加载Spring MVC的容器(主要管理Controller等Bean)。 - 字符编码过滤器:解决中文乱码问题,必须配置。
<!-- 字符编码过滤器 --> <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> <!-- Spring根容器配置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC前端控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-valu> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> - Spring的监听器:
- 配置Spring和MyBatis:这是整合的核心。
applicationContext.xml:配置数据源(Druid)、事务管理器、以及扫描Service和Dao层的Bean。spring-mvc.xml:配置扫描Controller、视图解析器(告诉Spring MVC JSP文件在哪里)、静态资源处理、开启注解驱动。mybatis-config.xml:配置MyBatis的全局设置,如驼峰命名映射、别名等。但很多配置可以合并到Spring配置中。- 关键整合步骤:在
applicationContext.xml中配置SqlSessionFactoryBean,将MyBatis的SqlSessionFactory交给Spring管理,并注入数据源和Mapper XML文件的位置。再配置MapperScannerConfigurer,让它自动扫描Dao接口,并为其创建Spring Bean。
这个过程就像搭积木,每一步都是下一层的基础。配置完成后,启动Tomcat,如果控制台没有报错,并能看到Spring和MyBatis初始化的日志,那么恭喜你,环境搭建成功了。
4. 核心功能实现与代码详解
4.1 实体类与Mapper层实现
一切从定义数据开始。首先创建Customer实体类,属性对应数据库字段,并生成getter/setter方法。这里可以使用Lombok插件简化代码。
然后创建Dao层接口CustomerMapper.java。这里只定义方法,不写实现。
public interface CustomerMapper { int insert(Customer customer); int deleteById(Integer id); int update(Customer customer); Customer selectById(Integer id); List<Customer> selectAll(); List<Customer> selectByCondition(@Param("name") String name, @Param("phone") String phone); }真正的SQL写在对应的XML文件CustomerMapper.xml中,放在resources/mappers目录下。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.yourcompany.crm.dao.CustomerMapper"> <resultMap id="BaseResultMap" type="com.yourcompany.crm.entity.Customer"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="gender" property="gender"/> <result column="phone" property="phone"/> <!-- 其他字段映射 --> </resultMap> <insert id="insert" parameterType="Customer" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_customer (name, gender, phone, email, ...) VALUES (#{name}, #{gender}, #{phone}, #{email}, ...) </insert> <select id="selectByCondition" resultMap="BaseResultMap"> SELECT * FROM t_customer <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="phone != null and phone != ''"> AND phone = #{phone} </if> </where> </select> </mapper>这里有个非常重要的技巧:<where>标签和<if>标签的使用。<where>标签会智能地处理WHERE关键字,如果里面所有的<if>条件都不成立,它不会生成WHERE,避免了WHERE后面直接跟AND的语法错误。<if>标签实现了动态SQL,根据传入的参数决定是否拼接某段SQL。这使得一个方法可以应对多种查询条件组合,非常灵活。
4.2 Service业务逻辑层封装
Service层是业务逻辑的核心。我们先定义接口CustomerService,再写实现类CustomerServiceImpl。这样做是为了面向接口编程,提高扩展性。
@Service // Spring注解,声明这是一个Service Bean public class CustomerServiceImpl implements CustomerService { @Autowired // Spring自动注入Mapper Bean private CustomerMapper customerMapper; @Override public boolean addCustomer(Customer customer) { // 业务校验:手机号格式、邮箱格式、非空检查等 if (customer.getPhone() == null || !isValidPhone(customer.getPhone())) { throw new IllegalArgumentException("手机号格式错误"); } // 检查手机号是否已存在 Customer exist = customerMapper.selectByPhone(customer.getPhone()); if (exist != null) { throw new RuntimeException("手机号已存在"); } // 设置创建时间等默认值 customer.setCreateTime(new Date()); customer.setUpdateTime(new Date()); // 调用Mapper插入数据 return customerMapper.insert(customer) > 0; } @Override @Transactional // 声明式事务注解,如果方法内发生异常,所有数据库操作回滚 public boolean updateCustomer(Customer customer) { // 更新前,先检查客户是否存在 Customer old = customerMapper.selectById(customer.getId()); if (old == null) { throw new RuntimeException("客户不存在"); } // 如果手机号有修改,需要检查新手机号是否已被他人使用 if (!old.getPhone().equals(customer.getPhone())) { Customer exist = customerMapper.selectByPhone(customer.getPhone()); if (exist != null) { throw new RuntimeException("新手机号已被其他客户使用"); } } customer.setUpdateTime(new Date()); return customerMapper.update(customer) > 0; } // ... 其他方法 }事务管理:注意@Transactional注解。在更新操作中,我们可能先查询再更新,这应该是一个原子操作。加上这个注解后,Spring会为这个方法开启一个数据库事务,如果方法执行成功则提交,如果抛出运行时异常则回滚,保证了数据的一致性。
4.3 Controller层与前后端交互
Controller层接收HTTP请求,调用Service,然后返回结果。它像是一个服务员,接收顾客(前端)的点单(请求),交给后厨(Service)处理,再把做好的菜(响应)端回去。
@Controller // 声明这是一个Controller @RequestMapping("/customer") // 类级别的映射,所有方法路径前都会加上/customer public class CustomerController { @Autowired private CustomerService customerService; // 跳转到客户列表页 @GetMapping("/list") public String listPage() { return "customer/list"; // 对应 /WEB-INF/jsp/customer/list.jsp } // 获取客户列表数据(JSON格式,供前端表格异步加载) @GetMapping("/api/list") @ResponseBody // 表示返回的是JSON数据,而不是视图名 public Map<String, Object> getCustomerList( @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, @RequestParam(value = "name", required = false) String name) { // 调用Service进行分页查询 PageHelper.startPage(pageNum, pageSize); // 使用PageHelper物理分页 List<Customer> list = customerService.getCustomersByName(name); PageInfo<Customer> pageInfo = new PageInfo<>(list); Map<String, Object> result = new HashMap<>(); result.put("success", true); result.put("data", pageInfo.getList()); // 当前页数据 result.put("total", pageInfo.getTotal()); // 总记录数 return result; } // 新增客户 @PostMapping("/api/add") @ResponseBody public Map<String, Object> addCustomer(@RequestBody Customer customer) { // @RequestBody接收JSON格式的请求体 Map<String, Object> result = new HashMap<>(); try { customerService.addCustomer(customer); result.put("success", true); result.put("message", "添加成功"); } catch (Exception e) { result.put("success", false); result.put("message", "添加失败: " + e.getMessage()); } return result; } }关键点解析:
@GetMapping,@PostMapping:这是Spring MVC的注解,分别映射HTTP的GET和POST请求。RESTful风格建议:获取数据用GET,新增用POST,更新用PUT,删除用DELETE。@ResponseBody:这个注解至关重要。它告诉Spring MVC,方法的返回值不是视图名,而是要直接写入HTTP响应体的数据(通常转换为JSON)。配合@RestController注解(等于@Controller+@ResponseBody)使用更简洁。@RequestBody:用于将前端发送的JSON字符串,自动绑定到后端的Java对象上。- 分页:我们使用了
PageHelper这个国产开源分页插件。它通过拦截MyBatis的SQL,自动拼接分页语句(LIMIT),非常方便。PageInfo对象包含了分页的所有信息(当前页、总页数、数据列表等)。
4.4 前端页面与数据绑定
前端我们使用简单的JSP + Bootstrap + jQuery。JSP负责页面骨架,Bootstrap负责样式,jQuery负责发起Ajax请求与后端交互。
在list.jsp中,我们会有一个表格来展示客户列表。
<table id="customerTable" class="table table-hover"> <thead><tr><th>ID</th><th>姓名</th><th>手机号</th><th>操作</th></tr></thead> <tbody><!-- 数据由JS动态加载 --></tbody> </table> <nav id="pagination"><!-- 分页组件 --></nav>使用jQuery的Ajax来加载数据:
function loadCustomerData(pageNum, name) { $.ajax({ url: '${pageContext.request.contextPath}/customer/api/list', type: 'GET', data: {pageNum: pageNum, pageSize: 10, name: name}, dataType: 'json', success: function(result) { if (result.success) { // 1. 清空表格 $('#customerTable tbody').empty(); // 2. 遍历数据,拼接HTML行 $.each(result.data, function(index, customer) { var row = '<tr>' + '<td>' + customer.id + '</td>' + '<td>' + customer.name + '</td>' + '<td>' + customer.phone + '</td>' + '<td>' + '<button class="btn btn-sm btn-primary" onclick="editCustomer(' + customer.id + ')">编辑</button> ' + '<button class="btn btn-sm btn-danger" onclick="deleteCustomer(' + customer.id + ')">删除</button>' + '</td>' + '</tr>'; $('#customerTable tbody').append(row); }); // 3. 渲染分页组件 renderPagination(result.total, pageNum); } } }); }前后端分离的雏形:虽然我们用了JSP,但数据是通过Ajax异步获取的JSON,页面渲染在浏览器端完成。这已经是前后端分离的一种简单形式。这样做的好处是页面无需刷新,用户体验好,后端Controller只负责提供数据接口,职责清晰。
5. 项目部署、测试与常见问题排查
5.1 项目打包与部署
开发完成后,需要将项目部署到服务器或交给老师验收。Maven的打包命令是mvn clean package。对于Web项目,会生成一个项目名.war文件。
- 本地Tomcat部署:将生成的
.war文件复制到Tomcat的webapps目录下,启动Tomcat,它会自动解压并部署。访问http://localhost:8080/你的项目名/即可。 - 外置Tomcat部署(IDEA):在IDEA的Run/Debug Configuration中配置Tomcat,指定
Deployment为Artifact,选择你项目生成的war包。这样可以在IDEA中直接启动和调试。 - 打可执行Jar包(Spring Boot方式):如果你的项目后期想改为Spring Boot,可以使用
spring-boot-maven-plugin打包成一个包含内嵌Tomcat的、可直接通过java -jar运行的Jar包,部署极其方便。但对于纯SSM项目,通常还是打War包。
5.2 功能测试要点
测试不能只靠眼睛看,要有计划。
- 单元测试:使用JUnit对Service层的方法进行测试。确保每个业务逻辑(如新增时的校验、更新时的检查)都正确。可以使用
@RunWith(SpringJUnit4ClassRunner.class)和@ContextConfiguration加载Spring配置,然后@Autowired注入Service进行测试。 - 接口测试:使用Postman或浏览器直接访问Controller的API接口,检查返回的JSON数据是否正确。重点测试边界情况:传入空值、超长字符串、不存在的ID等。
- 界面集成测试:在浏览器中手动操作,覆盖所有功能点:增、删、改、查、分页、条件搜索。检查页面交互是否流畅,错误提示是否友好。
- 数据库验证:在Navicat里直接查看数据表,确认操作后的数据状态是否符合预期,特别是时间戳字段。
5.3 常见问题与解决方案实录
以下是我在开发和教学过程中,学生遇到最高频的几个问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
启动Tomcat时报ClassNotFoundException或NoClassDefFoundError | 1. 依赖的Jar包没有正确引入。 2. Jar包版本冲突。 3. Tomcat提供的Servlet-api与项目中的冲突。 | 1. 检查pom.xml依赖,运行mvn clean compile看是否报错。2. 在IDEA中打开 Project Structure -> Modules -> Dependencies,查看是否有红色波浪线。3. 将 servlet-api的依赖范围设为provided,表示该包由Tomcat容器提供。 |
| 页面中文乱码 | 1. 数据库连接字符集不是UTF-8。 2. 请求/响应编码未设置。 3. JSP页面编码未设置。 | 1. 检查数据库、表、字段的字符集是否为utf8mb4。2. 确认 web.xml中配置了字符编码过滤器,且forceEncoding为true。3. 在JSP页面头部添加 <%@ page contentType="text/html;charset=UTF-8" language="java" %>。4. 在Spring MVC配置中设置 <mvc:annotation-driven>并配置消息转换器。 |
| 404错误(页面找不到) | 1. 请求路径错误。 2. DispatcherServlet的url-pattern配置问题。3. Controller方法未被扫描到。 | 1. 检查浏览器地址栏URL是否与@RequestMapping的值匹配。2. 检查 web.xml中DispatcherServlet的<url-pattern>,通常为/。3. 检查 spring-mvc.xml中组件扫描的包路径是否包含了Controller类。 |
| 500错误(服务器内部错误) | 1. 空指针异常(NPE)。 2. SQL异常。 3. 类型转换错误。 | 1.看控制台日志!Tomcat的catalina.out或IDEA的控制台会打印详细的异常堆栈信息,这是定位问题的关键。 2. 检查Service或Controller中自动注入( @Autowired)的对象是否为null。3. 检查MyBatis的SQL映射文件,参数 #{}中的属性名是否与实体类一致。 |
@Autowired注入失败,对象为null | 1. 该类没有被Spring管理(缺少@Component,@Service等注解)。2. 包路径不在Spring的扫描范围内。 3. 存在多个同类型的Bean。 | 1. 确保被注入的类(如CustomerServiceImpl)上有@Service注解。2. 检查 applicationContext.xml中的<context:component-scan>的base-package是否包含了该类的包。3. 使用 @Qualifier注解指定Bean的名称。 |
| MyBatis查询结果为空或字段映射失败 | 1. SQL语句有误。 2. 数据库连接错误,连到了别的库或表。 3. 实体类字段名与数据库列名不一致,未开启驼峰映射。 | 1. 将MyBatis的日志级别设为DEBUG,在控制台查看实际执行的SQL。 2. 检查 jdbc.properties中的数据库名、表名。3. 在MyBatis配置中设置 <setting name="mapUnderscoreToCamelCase" value="true"/>,开启自动驼峰命名映射(user_name->userName)。 |
事务@Transactional不生效 | 1. 方法不是public的。2. 异常类型不是运行时异常(RuntimeException)。 3. 在同一个类内部方法调用,走了代理对象。 | 1. 确保事务方法为public。2. 默认只回滚运行时异常和Error。可配置 @Transactional(rollbackFor = Exception.class)回滚所有异常。3. 避免在同一个Service类中,一个非事务方法调用另一个事务方法。 |
一个典型的排错流程:遇到问题 -> 1. 看浏览器控制台(F12)的Network和Console标签,看请求是否发出、状态码、响应内容。 -> 2. 看服务器(IDEA)控制台日志,找异常堆栈。 -> 3. 根据异常信息,定位到具体的代码行。 -> 4. 使用Debug模式,打断点,逐步执行,观察变量状态。
5.4 项目扩展与优化建议
如果你想让你的毕设脱颖而出,可以尝试以下扩展方向,这会在答辩时给你加分:
- 加入权限控制:使用Spring Security或Shiro框架,实现简单的登录、角色管理(如管理员和普通用户)、权限拦截。让系统更完整。
- 实现文件上传:允许为客户上传头像或合同附件。Spring MVC对文件上传有很好的支持,结合OSS(对象存储)会更专业。
- 添加数据导出功能:使用Apache POI或EasyExcel库,将客户列表导出为Excel文件。
- 引入缓存:对于不常变动的数据(如客户类型字典),可以使用Redis进行缓存,减轻数据库压力,提升查询速度。
- 前端框架升级:将JSP替换为更现代化的前端框架,如Vue.js或React,实现真正的前后端分离。后端Controller只提供RESTful API。
- 改为Spring Boot:尝试将项目迁移到Spring Boot,体验一下“约定大于配置”的便捷,对比SSM和Spring Boot的异同,这本身就是一个很好的学习点。
做这个项目,最重要的不是把代码敲完,而是在这个过程中,理解每一个配置的意义,每一行代码的作用,遇到问题并解决它。当你看到浏览器上成功显示出从数据库查出来的客户列表时,那种成就感就是学习编程最大的动力。这个项目做完,Java Web开发的大门,你就已经推开一半了。
本文还有配套的精品资源,点击获取