贯穿示例 · SSM 人事管理系统(SHrmSSM01B)
一个真实的 Spring+MVC+MyBatis 部门/员工管理系统:四层架构 + 四份配置文件如何拼成一个可运行的 Web 应用
贯穿示例 · SSM 人事管理系统(SHrmSSM01B)
贯穿式教学示例(三):前面两个示例是「从零手写」和「逐版演进」,本示例反过来——直接解剖一个已经跑起来的 SSM 综合项目
SHrmSSM01B(部门 + 员工管理 + 登录)。素材在本仓库同级工程里,下面代码与配置均摘自真实文件。这条线的意义:课程模块 4–11(Spring IoC / SpringMVC / MyBatis / SSM 整合 / 综合应用)的知识点,在这里全部落到一个可运行的工程上——你会看清「四层架构」和「四份配置文件」是怎么协作成一个 Web 应用的。
一、项目概览:部门 + 员工 + 登录
SHrmSSM01B 是一个经典的人事管理(HRM)系统,核心两张表:
-- SHrmSSM01B/shrmssm.sql(节选)
CREATE TABLE `tbl_dept` (
`dept_id` int(11) NOT NULL AUTO_INCREMENT,
`dept_name` varchar(255) DEFAULT NULL, -- 部门名
`dept_leader` varchar(255) DEFAULT NULL, -- 负责人
PRIMARY KEY (`dept_id`)
);
CREATE TABLE `tbl_emp` (
`emp_id` int(11) NOT NULL AUTO_INCREMENT,
`emp_name` varchar(255) DEFAULT NULL,
`emp_email` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL,
`department_id` int(11) DEFAULT NULL, -- 关联部门
PRIMARY KEY (`emp_id`)
);
功能面:登录(LoginController)、部门管理、员工管理。数据访问走 MyBatis(DepartmentMapper.xml 等),配置分四份。下面按「四层架构 + 四份配置」两条线拆开看。
交互实验:SSM 三层架构总览(SSMArchitectureMap)
点「下一步」沿请求链走一遍,或点击任一层看它的职责与配置归属。对照真实工程 GuestBookSSM(web.xml / springmvc-servlet.xml / applicationContext.xml / WordMapper.xml)。
浏览器
职责:发起 HTTP 请求,接收渲染后的 HTML 响应
二、Controller 层:统一入口在 /hrms
控制器用 @Controller + @RequestMapping("/hrms") 统一挂在 /hrms 前缀下,登录接口返回 JSON(@ResponseBody):
// SHrmSSM01B/src/com/hrms/controller/LoginController.java(节选)
@Controller
@RequestMapping(value = "/hrms")
public class LoginController {
// 登录:@ResponseBody + JsonMsg 返回统一 JSON 结果
}
MainController、TestController 同层。注意 Controller 只收请求、转发给 Service,不写业务逻辑——这是分层的纪律。
三、Service 层与 Dao 层:业务与数据访问分离
- Service 层(
com.hrms.service):承载业务逻辑与事务; - Dao/Mapper 层(
com.hrms.mapper+resources/com/hrms/mapper/*.xml):MyBatis 映射文件,如DepartmentMapper.xml,把 SQL 与实体解耦。
Controller → Service → Mapper 三层各依赖下一层的接口,互不越级——这正是模块 10(SSM 整合)强调的「分层可测」。
交互实验:MyBatis 一次查询全链路(MyBatisMapperFlow)
先选一个 DAO 方法,再点「下一步」,看一次 wordDao.findAll() 如何从接口调用一路走到 Java 对象。对照真实工程 GuestBookSSM(WordDao + WordMapper.xml)。
调用 DAO 接口 —— 负责组件 WordDao.findAll()
应用层像调普通 Java 方法一样调 wordDao.findAll()——接口里没有实现类,却能返回数据
#{...}:预编译占位符,MyBatis 把它转成 PreparedStatement 的 ?,参数与 SQL 分离,天然防 SQL 注入;${...}:字符串直接拼接进 SQL,用于动态表名/列名等无法预编译的场景,但必须校验输入,否则有注入风险。
四、四份配置文件:它们如何拼成一个应用
SHrmSSM01B 用四份 XML分工协作,这是 SSM 项目的骨架。理解它们的加载顺序,就理解了整个容器是怎么启动的。
交互实验:四配置文件加载关系(SSMConfigMap)
数字是加载顺序。点击任一文件看「谁加载它、里面配了什么」。对照真实工程 GuestBookSSM(web.xml / applicationContext.xml / springmvc-servlet.xml / mybatis-config.xml)。
web.xml —— 由 Tomcat 容器启动时最先读 加载
职责:注册 ContextLoaderListener + DispatcherServlet + Filter(编码/权限)
applicationContext.xml 由 ContextLoaderListener 加载,是 Spring「根容器」(管 Service/DAO/数据源/事务);springmvc-servlet.xml 由 DispatcherServlet 加载,是「Web 子容器」(只管 Controller 与视图解析),它能看到根容器的 Bean。
1) web.xml——容器的总开关
<!-- SHrmSSM01B/WebContent/WEB-INF/web.xml(节选) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value> <!-- ① 根容器 -->
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value> <!-- ② MVC 子容器 -->
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern> <!-- 拦截所有请求 -->
</servlet-mapping>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- utf-8,放最前 -->
两个关键点:
ContextLoaderListener启动时加载applicationContext.xml(根容器:Service、Dao、数据源);DispatcherServlet再加载springmvc.xml(MVC 子容器:Controller、视图解析器),并拦截/下所有请求。
2) applicationContext.xml——根容器:扫描业务 Bean
<!-- SHrmSSM01B/resources/applicationContext.xml -->
<context:component-scan base-package="com.hrms">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/> <!-- Controller 留给 MVC 容器 -->
</context:component-scan>
<import resource="MyBatis.xml"/> <!-- 引入 MyBatis 配置 -->
注意 exclude-filter:根容器扫描 com.hrms 但排除 @Controller——Controller 由 MVC 子容器单独管理,避免两个容器重复创建。
3) springmvc.xml——MVC 子容器:注解驱动 + 视图解析
<!-- SHrmSSM01B/resources/springmvc.xml -->
<mvc:annotation-driven/>
<context:component-scan base-package="com.hrms.controller"/> <!-- 只扫 Controller -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
@Controller 在这里被扫描注册;返回的视图名(如 login)由解析器拼成 /WEB-INF/jsp/login.jsp。
4) MyBatis.xml——数据源 + SqlSession + Mapper 扫描
<!-- SHrmSSM01B/resources/MyBatis.xml -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/shrmssm?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hrms.mapper"/> <!-- 扫描 Mapper 接口 -->
</bean>
MapperScannerConfigurer 扫描 com.hrms.mapper 包,把每个 Mapper 接口的动态代理注册成 Bean——Controller/Service 里 @Autowired 到的 Dao 就是这么来的。
五、启动顺序:一图看懂四份配置的加载链
| 步骤 | 谁触发 | 加载什么 |
|---|---|---|
| 1 | Tomcat 启动 WebApp | web.xml:注册监听器与 Servlet |
| 2 | ContextLoaderListener | applicationContext.xml(根容器)→ import MyBatis.xml |
| 3 | DispatcherServlet | springmvc.xml(MVC 子容器,扫 Controller) |
| 4 | MapperScannerConfigurer | 扫描 com.hrms.mapper,生成 Mapper 代理 Bean |
| 5 | 请求到达 | DispatcherServlet → Controller → Service → Mapper → 数据库 |
人事管理系统功能地图(SHrmSSM01B)
SHrmSSM01B 是实验八的「进行中」工程:登录与部门分页/编辑已落地,新增/删除与员工模块四层开发待补全——功能地图既是需求分析素材,也是实验八的工作清单。
打开站点直接进入登录页(welcome-file),提交用户名/密码表单。
- 配置
WebContent/WEB-INF/web.xml<welcome-file>把 /WEB-INF/jsp/login.jsp 设为启动页
- 表现层
src/com/hrms/controller/LoginController.javalogin()/hrms/login(GET)返回视图名 login
- 表现层
WebContent/WEB-INF/jsp/login.jsplogin.jsp登录表单(username / password)
六、小结:SSM = 分层 + 配置分工
- 分层:Controller(收请求)/ Service(业务+事务)/ Mapper(数据访问)/ Entity(数据模型),各依赖下一层接口;
- 配置分工:
web.xml定总开关,applicationContext.xml管根容器(排除 Controller),springmvc.xml管 MVC 子容器与视图,MyBatis.xml管数据源与 Mapper 扫描。
把课程模块 4–11 的概念对号入座到这里,一个 SSM 项目「为什么长这样」就清楚了——不是照抄模板,而是分层与配置分工的自然结果。
学习小测验
在 SHrmSSM01B 中,DispatcherServlet 加载的是哪份配置?
applicationContext.xml 里对 @Controller 使用 exclude-filter 的原因是( )
Service/Controller 里 @Autowired 到的 Mapper(Dao)Bean 是由谁生成的?
返回视图名 \"login\" 最终被解析成哪个 JSP?