JavaEE核心架构图:MyBatis-Spring-SpringMVC-AOP技术栈贯通解析

发布时间:2026/10/23 20:59:44
JavaEE核心架构图:MyBatis-Spring-SpringMVC-AOP技术栈贯通解析

简介:本资源是面向高校计算机专业学生与JavaEE初学者的期末复习题库,聚焦MyBatis框架核心知识点,系统覆盖ORM原理、SQL映射配置、事务管理、模糊查询安全、动态SQL及工作流程等高频考点,助力高效备考与概念巩固。压缩包为单个41KB的Word文档(.docx),内容结构清晰,含填空、判断、选择、简答四类题型,每章配套标准答案与解析,如第1章深入考查 与 元素用法、namespace作用、concat()函数局限性及MyBatis八步工作原理;第2章延伸至 映射、 代码复用、数据源类型等进阶配置。题目设计紧扣教学重点,强调易错点辨析与原理理解,避免死记硬背。目前已有2646人学习下载,适合作为期末冲刺自测、课堂随堂练习或MyBatis模块化复习的权威辅助材料。

1. JavaEE期末复习:一张贯穿 MyBatis → Spring → Spring MVC → AOP 的知识地图

如果你正在翻着《JavaEE期末复习题库》发愁——填空题空了三行、判断题反复纠结“对/错”、选择题四个选项看着都像对的、简答题写到一半突然卡壳……别急,这不是你学得差,而是这套题库背后藏着一条被拆解得过于细碎、却从未被系统串联的知识主干。它不是零散考点的堆砌,而是一张从数据持久层(MyBatis)出发,经由控制反转容器(Spring Core),再向上延伸至 Web 层(Spring MVC)与横切关注点(AOP)的完整技术栈地图。这张图里,<mapper>不是孤立标签,它是 ORM 映射逻辑的起点;@Autowired不是魔法注解,它是 IoC 容器生命周期管理的具象化;DispatcherServlet更不是黑匣子,它是整个 MVC 流程的调度中枢。真正让你在考前夜反复出错的,往往不是某个单词拼写,而是没看清这个模块在整个 JavaEE 架构中“站在哪一级、连着哪一根线、承接着谁的输入、输出给谁用”。本篇笔记就按这张地图来梳理:不罗列定义,不复述答案,而是把每道题背后隐含的设计意图、执行路径、易混淆边界和真实运行时行为全部摊开。适合两类人:一是刚学完各模块、但感觉知识点像散落珠子的同学;二是刷过几轮题、却总在相似位置翻车、急需一次结构化归因的备考者。我们不讲“应该背什么”,只说“为什么必须这样理解”。

MyBatis:不是 SQL 拼接器,而是对象-关系映射的编排引擎

MyBatis 的本质,是让 Java 对象能“自然地”活在关系型数据库之上。它的核心不是帮你写 SQL,而是精准控制 SQL 如何生成、参数如何绑定、结果如何映射、事务如何收口。所以<delete>元素之所以重要,不是因为它叫 delete,而是因为它强制你意识到:删除操作必须显式提交事务(否则数据白删),且其 SQL 执行路径与<select>截然不同——前者走Executor.update(),后者走Executor.query(),底层调用 JDBC 的executeUpdate()与executeQuery()。同样,namespace也不是为了“起个名字”,而是为SqlSession.getMapper(XxxMapper.class)提供类型安全的代理入口——没有它,MyBatis 就无法将接口方法名准确路由到对应 XML 中的<delete id="xxx">。这就是为什么填空题第一问直指<delete>,第二问紧跟着namespace:它们共同构成 MyBatis 最小可运行单元的骨架。

Spring Core:不是配置文件搬运工,而是对象生命周期的中央控制器

当你看到ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"),别只把它当一行初始化代码。这行代码背后,是 Spring 容器启动的完整生命周期:解析 XML → 实例化 BeanDefinition → 推断构造函数 → 调用InstantiationAwareBeanPostProcessor→ 注入属性(@Autowired触发AutowiredAnnotationBeanPostProcessor)→ 调用InitializingBean.afterPropertiesSet()→ 执行init-method→ 放入单例池。而prototype作用域的 Bean,恰恰在“放入单例池”这一步被截断——容器只负责创建,不负责后续管理。所以判断题里“Spring 只负责创建 prototype Bean”是对的,但“每次请求 singleton 都新建实例”就是典型误解。这种认知偏差,直接导致你在做“Bean 作用域”类选择题时,在request和prototype之间反复横跳。

Spring MVC:不是 Servlet 封装器,而是 HTTP 请求的标准化流水线

DispatcherServlet是整个流程的“总调度台”,但它本身不处理业务逻辑。它严格遵循“接收 → 映射 → 适配 → 执行 → 解析 → 渲染 → 响应”的十步流水线。其中HandlerMapping决定“谁来处理这个 URL”,HandlerAdapter解决“怎么调用这个 Handler(Controller 方法)”,ViewResolver负责“把逻辑视图名变成真实页面路径”。所以填空题问“前端控制器拦截后调用什么”,答案是HandlerMapping,而不是Controller或ViewResolver——因为这是流水线的第一道分拣口。而@RequestMapping的 method 属性之所以“可选”,正是因为DispatcherServlet默认支持所有 HTTP 方法;但一旦你写了@GetMapping,它就自动绑定了method = RequestMethod.GET,这是注解的语义糖,不是功能增强。

AOP:不是日志打印工具,而是横切逻辑的织入协议

@Aspect类里的@Before方法,执行时机永远在目标方法之前,但“之前”不等于“立刻之前”——如果目标方法抛异常,@AfterReturning就不会执行,而@After(最终通知)一定执行。这种差异,源于 Spring AOP 的底层实现:它通过ProxyFactoryBean创建 JDK 动态代理或 CGLIB 代理,在目标方法调用前后插入MethodInterceptor链。而@Pointcut定义的切入点表达式(如execution(* com.xxx.service.*.*(..))),本质是告诉代理:“当执行这些方法时,请把我的通知加进去”。所以选择题里问“AOP 是 OOP 的升级替代品”,答案必错——AOP 是 OOP 的补充,它解决的是分散在各处的重复逻辑(日志、事务、权限),而非替代封装、继承、多态。

复习策略:拒绝题海,转向“路径验证式学习”

与其反复刷同一道“<resultMap>子元素有哪些”的填空题,不如打开一个真实项目,做三件事:

  1. 在mybatis-config.xml中找到<mappers>,确认它加载了哪个Mapper.xml;
  2. 在该Mapper.xml中找到一个<select>,看它的resultMap属性指向哪个<resultMap>;
  3. 在<resultMap>中逐行检查<id>和<result>的column与property是否匹配 Java 类字段。
    这三步走完,你记住的不是“<id>是主键”,而是“当数据库主键列名和 Java 字段名不一致时,<id column='user_id' property='id'>是唯一能打通映射的桥梁”。这才是题库该有的打开方式——它不是考卷,而是你调试 JavaEE 架构的探针。

2. MyBatis 核心机制:从 XML 配置到动态 SQL 的执行链路

MyBatis 的学习常陷入两个误区:一是把 XML 当成静态配置文档,忽略其与 Java 对象、SQL 执行、事务管理的实时联动;二是把动态 SQL 当成模板语法糖,没意识到<if>、<set>等标签背后是 OGNL 表达式引擎与 SQL 构建器的深度协作。本章不罗列标签用法,而是沿着一条真实请求的执行路径,拆解 MyBatis 如何把一个userMapper.deleteByStatus("inactive")调用,一步步翻译成可执行的 JDBC 操作。这条路径覆盖了从全局配置加载、映射文件解析、SQL 动态组装、参数绑定,到最终执行的全链路,也是期末题库中填空、判断、选择题最密集的出题区。

2.1 全局配置与映射加载:mybatis-config.xml不是摆设,而是运行时环境的契约

mybatis-config.xml是 MyBatis 的“宪法”,它定义了框架运行的底线规则。其中最关键的配置项,远不止“获取数据库连接”这么简单:

提示:<environments>中的default属性值,必须与<environment id="xxx">的id完全一致,否则SqlSessionFactoryBuilder.build()会抛IllegalArgumentException。这是填空题第 4 题“主要内容是获取【 】”的答案陷阱——表面答“数据库连接”,实则要意识到:连接只是DataSource配置的结果,而environments还决定了事务管理器类型(JDBC 或 MANAGED)、是否开启延迟加载等全局行为。

<!-- mybatis-config.xml 片段 --> <configuration> <properties resource="db.properties"/> <!-- 外部属性文件,支持占位符 ${driver} --> <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> <typeAliases> <typeAlias alias="User" type="com.example.entity.User"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>

这段配置的执行顺序是:

  1. 加载db.properties,注入${}占位符;
  2. 初始化Settings,影响后续所有 SQL 执行行为(如lazyLoadingEnabled=true时,关联查询会触发懒加载);
  3. 注册别名User,使UserMapper.xml中可直接写resultType="User"而非全限定名;
  4. 构建Environment,创建PooledDataSource(连接池)和JdbcTransactionFactory;
  5. 最后,扫描并加载UserMapper.xml—— 注意:<mappers>必须放在</environments>之后,否则解析失败。

这就是为什么题库中“<mappers>是配置文件根元素”是错的(判断题第 1 题):<configuration>才是根,<mappers>只是其子元素。而namespace属性(填空题第 2 题)之所以必须存在,是因为SqlSession.getMapper(UserMapper.class)依赖它来定位UserMapper.xml中的 SQL 片段。没有namespace,MyBatis 就无法将接口方法deleteByStatus(String status)映射到<delete id="deleteByStatus">。

2.2 SQL 映射与参数绑定:#{}与${}的生死线

MyBatis 最易错的点,集中在参数传递的安全性上。题库中多次出现concat()函数与 SQL 注入的辨析(填空题第 3 题、选择题第 3 题),其核心在于理解 MyBatis 的两种参数处理机制:

参数写法底层实现安全性典型场景风险示例
#{status}预编译参数(?占位符),JDBCPreparedStatement.setString(1, "inactive")✅ 防 SQL 注入所有常规条件查询、更新、删除WHERE status = ?→ 安全
${status}字符串拼接(OGNL 表达式求值后直接插入 SQL)❌ 高危!可注入动态表名、列名、ORDER BY 子句WHERE status = 'inactive'; DROP TABLE user;'→ 爆库

因此,填空题第 3 题“使用 MySQL 中的【 】函数进行字符串拼接,可以有效防止模糊查询时可能出现的 SQL 注入问题”是一个误导性陷阱。concat()函数本身不能防止注入!它只是 MySQL 的字符串函数。真正防注入的是#{}绑定。例如模糊查询应写为:

<!-- 安全写法:参数绑定 + 数据库函数 --> <select id="findByName" resultType="User"> SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%') </select>

而非:

<!-- 危险写法:字符串拼接 --> <select id="findByName" resultType="User"> SELECT * FROM user WHERE name LIKE '%${name}%' </select>

后者若传入name="a%' OR '1'='1",SQL 变成WHERE name LIKE '%a%' OR '1'='1%',直接绕过条件。

2.3 动态 SQL 的执行逻辑:<where>、<set>不是语法糖,而是 SQL 构建器

动态 SQL 的本质,是 MyBatis 在运行时根据 Java 对象状态,动态生成合法 SQL 字符串。<where>和<set>标签的核心价值,在于解决手工拼接 SQL 时的“多余 AND”、“多余逗号”问题。但它们的实现原理常被忽略:

  • <where>标签:当其内部所有<if>判断均为 false 时,整个<where>标签内容被忽略,不会生成WHERE关键字;若至少一个<if>为 true,则自动添加WHERE,并智能去除第一个AND或OR。
  • <set>标签:同理,当内部无<if>为 true 时,不生成SET;否则添加SET,并自动去除最后一个逗号。

看一个典型更新场景:

<update id="updateUser" parameterType="User"> UPDATE user <set> <if test="name != null and name != ''"> name = #{name}, </if> <if test="email != null and email != ''"> email = #{email}, </if> <if test="status != null"> status = #{status} </if> </set> WHERE id = #{id} </update>

若传入User{name=null, email="a@b.com", status="active"},MyBatis 生成的 SQL 是:

UPDATE user SET email = ?, status = ? WHERE id = ?

注意:<set>自动去掉了email = ?后面的逗号,并未生成SET , email = ?, status = ?这种语法错误。这就是填空题第 3 题“如果<set>元素内包含的内容都为空,则会出现【 】”的答案——SQL 语法错误(因为UPDATE user SET WHERE id = ?是非法的)。而填空题第 5 题“<set>元素就会动态前置【 】关键字,同时也会消除 SQL 语句中最后一个多余的【 】”,答案是SET和逗号。

2.4 结果映射:<resultMap>是对象与数据库的“翻译官”,不是可有可无的备胎

当数据库列名(user_name)与 Java 字段名(userName)不一致,或存在一对一、一对多关联时,<resultMap>是唯一解。题库填空题第 1 题“当数据表中的列和需要返回的对象的属性不完全一致,MyBatis 是不会自动赋值的。此时,就可以使用【 】元素进行处理”,答案是<resultMap>,但关键在于理解它的工作机制:

<resultMap id="UserResultMap" type="User"> <id property="id" column="user_id"/> <!-- 主键映射,必须!用于缓存和延迟加载 --> <result property="userName" column="user_name"/> <!-- 普通字段映射 --> <association property="dept" javaType="Department" resultMap="DeptResultMap"/> <collection property="roles" ofType="Role" resultMap="RoleResultMap"/> </resultMap>
  • <id>标签:标记主键列,必须配置。MyBatis 用它做一级缓存 Key 和延迟加载标识。若省略,关联查询可能失效。
  • <result>标签:映射普通字段,column是数据库列名,property是 Java 字段名。
  • <association>/<collection>:处理关联对象,resultMap属性指向另一个<resultMap>,实现嵌套结果映射。

这就是为什么判断题第 4 题“映射文件中可以包含多个 id 值相同的<resultMap>元素”是错的——id是<resultMap>的唯一标识,重复会导致XMLParser解析异常。

2.5 事务管理:MyBatis 的 CRUD 操作,从来不是“自动提交”

一个致命误区:认为 MyBatis 的insert()、update()、delete()执行后数据就落地了。事实是:所有修改操作都必须显式提交事务,否则回滚。题库判断题第 4 题“MyBatis 的更新操作也需要进行事务提交”为正确,选择题第 1 题 D 选项“删除操作和添加操作一样,也需要封装整个实体类”为错误,原因在此:

// 错误示范:忘记 commit SqlSession session = sqlSessionFactory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); mapper.deleteByStatus("inactive"); // SQL 已执行,但未提交! // session.commit(); // 忘记这一行 → 事务回滚,数据未删除 // 正确示范:try-with-resources + 显式 commit try (SqlSession session = sqlSessionFactory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); mapper.deleteByStatus("inactive"); session.commit(); // 必须! } catch (Exception e) { session.rollback(); // 异常时回滚 }

MyBatis 默认使用 JDBC 事务,openSession()返回的SqlSession默认autoCommit=false。这也是为什么题库反复强调“事务提交”——它不是加分项,是及格线。


3. Spring Core:IoC 容器的生命周期与 Bean 的装配哲学

Spring Core 的灵魂是 IoC(Inversion of Control,控制反转)与 DI(Dependency Injection,依赖注入)。但很多同学把@Autowired当成“自动赋值开关”,把ApplicationContext当成“对象工厂”,却忽略了 Spring 容器本身是一个有严格生命周期、状态管理和装配契约的运行时环境。本章聚焦三个核心:容器启动的完整流程、Bean 的作用域本质、以及装配方式的选择逻辑。这些正是题库中填空、判断、选择题高频踩坑区——比如“@Component标注在方法上”(判断题第 1 题)、“singleton每次请求都新建”(判断题第 3 题)、“autowire属性取值”(选择题第 1 题)等,根源都在对容器机制理解不深。

3.1 容器启动:从 XML 解析到 Bean 实例化的七步法

ClassPathXmlApplicationContext("applicationContext.xml")的执行,绝非简单加载配置。它是一套精密的七步初始化流程:

  1. 资源定位:ClassPathResource从 classpath 加载applicationContext.xml;
  2. XML 解析:XmlBeanDefinitionReader解析<bean>标签,生成BeanDefinition对象(存储 class、scope、properties 等元数据);
  3. 注册 BeanDefinition:存入DefaultListableBeanFactory的beanDefinitionMap;
  4. 实例化 BeanFactoryPostProcessor(如PropertyPlaceholderConfigurer),允许修改BeanDefinition;
  5. 实例化 Bean:按 scope 创建实例:
    • singleton:立即创建,存入singletonObjects缓存;
    • prototype:仅注册定义,调用getBean()时才创建;
  6. 属性填充(DI):AutowiredAnnotationBeanPostProcessor扫描@Autowired,通过BeanFactory.getBean()注入依赖;
  7. 初始化回调:调用InitializingBean.afterPropertiesSet()和init-method。

这个流程解释了题库中所有“容器行为”类题目:

  • 判断题第 2 题“通常在 Java 项目中,会采用FileSystemXmlApplicationContext”是错的——FileSystemXmlApplicationContext从文件系统加载,而ClassPathXmlApplicationContext从 classpath 加载,后者才是标准实践;
  • 填空题第 2 题“对象的实例不再由调用者来创建,而是由【 】来创建”,答案是Spring 容器,但更精确地说,是DefaultListableBeanFactory;
  • 选择题第 4 题“Spring 的核心容器是其他模块建立的基础,以下哪个不是该容器的组成模块”,答案是AOP 模块——因为 AOP 是独立模块,通过AopNamespaceHandler集成到容器,而非容器原生组件。

3.2 Bean 作用域:singleton与prototype的本质区别不在“数量”,而在“管理权”

scope属性决定 Spring 容器对 Bean 实例的管理粒度。题库填空题第 4 题“对于【 】作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 实例后,Bean 的实例就交给客户端代码来管理”,答案是prototype。但关键要理解“管理权移交”的含义:

作用域创建时机存储位置容器管理典型用途题库陷阱
singleton容器启动时(或首次getBean())singletonObjects缓存✅ 全生命周期管理(创建、注入、初始化、销毁)无状态服务类(Service、DAO)判断题第 3 题“每次请求都新建” → 错!
prototype每次getBean()调用时无缓存,由 GC 回收❌ 仅创建,不管理后续(不调用destroy-method)有状态对象(如用户会话上下文)判断题第 4 题“prototypeBean 在容器中只有一个实例” → 错!

因此,“singleton是单例”不等于“JVM 中只有一个对象”——它特指Spring 容器内唯一。而prototypeBean 的销毁,需由开发者手动处理(如调用close()),容器不会介入。这也是填空题第 4 题强调“交给客户端代码来管理”的深意。

3.3 装配方式:@Autowired的默认策略是byType,不是byName

Spring 提供三种装配方式:基于 XML 的设值注入/构造注入、基于注解的@Autowired/@Resource、以及自动装配(autowire属性)。题库选择题第 1 题“<bean>元素中的autowire属性取值不包括以下”,选项 DbyId是干扰项——autowire合法值只有no、byName、byType、constructor、autodetect,没有byId。

而@Autowired的工作原理是:

  • 默认按byType匹配(即查找相同类型的 Bean);
  • 若找到多个,再按byName匹配(字段名与 Bean 的id或name一致);
  • 可用@Qualifier("xxx")强制指定 Bean 名。
@Service public class UserServiceImpl implements UserService { ... } @Service("userServiceImpl2") public class UserServiceImpl2 implements UserService { ... } @Component public class UserController { @Autowired // 按 byType 找 UserService,但找到两个! private UserService userService; // 报错:NoUniqueBeanDefinitionException @Autowired @Qualifier("userServiceImpl2") // 指定名称,解决歧义 private UserService userService2; }

这就是为什么题库判断题第 1 题“@Component注解...只需将该注解标注在相应方法上即可”是错的——@Component是类级别注解,标注在方法上无效;而选择题第 4 题 A 选项“@Autowired默认按照 Bean 的名称进行装配”也是错的——默认是byType。

3.4 注解体系:@Repository、@Service、@Controller是语义化标签,非功能增强

Spring 的分层注解(@Repository、@Service、@Controller)本质是@Component的派生,功能完全等价,唯一区别是语义和 AOP 切面的默认切入点。题库填空题第 2、3 题分别考察@Controller作用于Controller层、@Repository作用于 DAO 层,这不仅是记忆点,更是设计规范:

  • @Repository:被PersistenceExceptionTranslationPostProcessor增强,自动将 JDBC 异常转为 Spring 的DataAccessException层次结构;
  • @Service:约定俗成的业务逻辑层,便于 AOP 切入事务(@Transactional);
  • @Controller:被@ResponseBody、@RequestBody等 Web 注解支持,专用于 MVC 控制器。

因此,选择题第 4 题 A 选项“@Autowired默认按照 Bean 的名称进行装配”错误,而 D 选项“@Controller用于将控制层的类标识为 Spring 中的 Bean”正确——它强调的是“标识为 Bean”,而非“提供 Web 功能”。

3.5 工厂模式:实例工厂与静态工厂,谁持有创建权?

Spring 支持三种 Bean 实例化方式:构造器、静态工厂、实例工厂。题库填空题第 5 题“实例工厂方式...需要实例化的 Bean 是通过【 】属性指向配置的实例工厂”,答案是factory-bean。关键区别在于:

  • 静态工厂:工厂类有静态方法,<bean>用class指向工厂类,factory-method指向静态方法名;
  • 实例工厂:工厂类需先被容器管理,<bean>用factory-bean指向工厂 Bean 的id,factory-method指向其实例方法名。
<!-- 静态工厂 --> <bean id="userDao" class="com.example.factory.DaoFactory" factory-method="createUserDao"/> <!-- 实例工厂 --> <bean id="daoFactory" class="com.example.factory.DaoFactory"/> <bean id="userDao2" factory-bean="daoFactory" factory-method="createUserDao"/>

选择题第 5 题“不属于 Spring 中实例化 Bean 的方式的是”,答案是抽象方法实例化——Spring 不支持抽象类直接实例化,必须有具体实现。


4. Spring MVC:从前端控制器到视图解析的十步执行流

Spring MVC 不是简单的“Controller 接收请求、返回视图”,而是一套高度解耦、职责分明的 HTTP 请求处理流水线。DispatcherServlet是这条流水线的总控中心,但它自身不写业务逻辑,所有工作都委托给一系列策略接口(HandlerMapping、HandlerAdapter、ViewResolver等)。题库中大量题目(如填空题第 1、3、4 题,判断题第 1、2 题,选择题第 2、3 题)都在考察你是否看清了每个环节的输入、输出和职责边界。本章以一次GET /user/list请求为例,完整拆解这十步执行流,让你彻底告别“DispatcherServlet 之后就模糊了”的复习困境。

4.1 执行流程全景:十步不可逆的请求流转

Spring MVC 的标准执行流程(依据题库简答题第 1 题答案)如下,每一步都是强依赖前一步的输出:

  1. 用户请求到达:浏览器发送GET http://localhost:8080/user/list;
  2. 前端控制器拦截:DispatcherServlet(在web.xml中配置为url-pattern="/")捕获请求;
  3. 处理器映射:DispatcherServlet调用HandlerMapping(如RequestMappingHandlerMapping),根据 URL/user/list查找匹配的@Controller方法,返回HandlerExecutionChain(含HandlerMethod和拦截器);
  4. 处理器适配:DispatcherServlet调用HandlerAdapter(如RequestMappingHandlerAdapter),将HandlerMethod适配为可执行对象;
  5. 执行处理器:HandlerAdapter调用UserController.list()方法,执行业务逻辑;
  6. 返回 ModelAndView:list()方法返回ModelAndView("user/list", model),其中"user/list"是逻辑视图名;
  7. 视图解析:DispatcherServlet调用ViewResolver(如InternalResourceViewResolver),将逻辑视图名"user/list"解析为真实路径/WEB-INF/jsp/user/list.jsp;
  8. 视图渲染:ViewResolver返回View对象,DispatcherServlet调用view.render(model, request, response),将模型数据填充到 JSP;
  9. 响应输出:JSP 渲染 HTML,写入HttpServletResponse输出流;
  10. 流程结束:DispatcherServlet返回,容器关闭请求。

这个流程解释了所有高频考点:

  • 填空题第 1 题“前端控制器拦截请求后,会调用【 】”,答案是HandlerMapping(第 3 步);
  • 填空题第 4 题“Spring MVC 提供了一个前端控制器【 】”,答案是DispatcherServlet(第 2 步);
  • 判断题第 1 题“HandlerAdapter 将 ModelAndView 对象返回给 ViewReslover”是错的——是DispatcherServlet调用ViewResolver(第 6→7 步),HandlerAdapter只负责执行 Controller 并返回ModelAndView(第 5→6 步);
  • 选择题第 2 题“Spring MVC 中的后端控制器是指”,答案是Handler(即UserController),而非DispatcherServlet(前端控制器)。

4.2 核心组件职责:HandlerMapping、HandlerAdapter、ViewResolver的分工铁律

这三个组件是 Spring MVC 的“三驾马车”,职责绝不交叉:

组件职责输入输出题库关联
HandlerMappingURL 到处理器的路由HTTP 请求(URL、HTTP 方法)HandlerExecutionChain(含HandlerMethod)填空题第 1 题、选择题第 3 题“负责拦截用户请求的是” → 答案是前端控制器,但HandlerMapping是它调用的第一个组件
HandlerAdapter处理器的统一调用适配HandlerExecutionChainModelAndView判断题第 1 题错误点:它不返回给ViewResolver,只返回给DispatcherServlet
ViewResolver逻辑视图名到物理视图的转换ModelAndView中的视图名View对象(如InternalResourceView)填空题第 3 题“可以配置...和【 】”,答案是视图解析器

特别注意:HandlerAdapter的存在,是为了兼容不同风格的处理器(如@Controller、HttpRequestHandler、Servlet),它把千差万别的处理器调用方式,统一成ModelAndView输出。没有它,DispatcherServlet就要为每种处理器写一套调用逻辑。

4.3 注解驱动开发:@RequestMapping的属性体系与语义缩写

@RequestMapping是 Spring MVC 的基石注解,但题库中对其属性的考察非常细致。填空题第 1 题“@RequestMapping注解类型用于映射【 】”,答案是URL,但实际它可映射多种维度:

属性说明示例题库考点
value/path指定 URL 路径@RequestMapping("/user")填空题第 1 题
method指定 HTTP 方法@RequestMapping(value="/user", method=RequestMethod.POST)判断题第 2 题“method属性必须使用” → 错!可选,默认支持所有方法
params指定请求参数约束@RequestMapping(params="action=save")未在题库出现,但属重点
headers指定请求头约束@RequestMapping(headers="Content-Type=application/json")未在题库出现

而@GetMapping、@PostMapping等是@RequestMapping的语义化缩写:

@GetMapping("/user") // 等价于 @RequestMapping(value = "/user", method = RequestMethod.GET)

判断题第 3 题“@RequestMapping(method = RequestMethod.GET)可以缩写为@GetMapping”为正确。这不仅是语法糖,更是 RESTful 风格的强制约定。

4.4 视图解析器:InternalResourceViewResolver的前缀/后缀魔法

ViewResolver的核心任务是把逻辑视图名(如"success")变成物理路径(如"/WEB-INF/jsp/success.jsp")。最常用的是InternalResourceViewResolver,它通过prefix和suffix属性完成拼接:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> `` <p> <a href="https://download.csdn.net/download/m0_52861684/88680769" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>