JavaEE 环境与程序设计 · 交互课程

贯穿示例 · 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 响应

SpringMVC:管 Web 层——DispatcherServlet 分发、Controller 处理请求、ViewResolver 渲染视图
Spring:管业务层——IoC 容器装配 Bean、@Transactional 声明式事务
MyBatis:管持久层——Mapper 接口 + XML 写 SQL、resultMap 映射结果集

二、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 层:业务与数据访问分离

Controller → Service → Mapper 三层各依赖下一层的接口,互不越级——这正是模块 10(SSM 整合)强调的「分层可测」。

交互实验:MyBatis 一次查询全链路(MyBatisMapperFlow)

先选一个 DAO 方法,再点「下一步」,看一次 wordDao.findAll() 如何从接口调用一路走到 Java 对象。对照真实工程 GuestBookSSM(WordDao + WordMapper.xml)。

调用 DAO 接口
→
动态代理拦截
→
namespace+id 定位 SQL
→
参数映射
→
执行 SQL
→
结果映射成对象

调用 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,放最前 -->

两个关键点:

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&amp;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 就是这么来的。

五、启动顺序:一图看懂四份配置的加载链

步骤谁触发加载什么
1Tomcat 启动 WebAppweb.xml:注册监听器与 Servlet
2ContextLoaderListenerapplicationContext.xml(根容器)→ import MyBatis.xml
3DispatcherServletspringmvc.xml(MVC 子容器,扫 Controller)
4MapperScannerConfigurer扫描 com.hrms.mapper,生成 Mapper 代理 Bean
5请求到达DispatcherServlet → Controller → Service → Mapper → 数据库

人事管理系统功能地图(SHrmSSM01B)

已实现(5)进行中(1)待实现(3)

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 = 分层 + 配置分工

把课程模块 4–11 的概念对号入座到这里,一个 SSM 项目「为什么长这样」就清楚了——不是照抄模板,而是分层与配置分工的自然结果。

学习小测验

Q1

在 SHrmSSM01B 中,DispatcherServlet 加载的是哪份配置?

Q2

applicationContext.xml 里对 @Controller 使用 exclude-filter 的原因是( )

Q3

Service/Controller 里 @Autowired 到的 Mapper(Dao)Bean 是由谁生成的?

Q4

返回视图名 \"login\" 最终被解析成哪个 JSP?