<servlet-class>和</url-pattern>的作用
在 SpringMVC 的 web.xml 配置中,<servlet-class> 和 <url-pattern> 是两个关键配置项,分别用于指定处理请求的 Servlet 类和定义该 Servlet 拦截的请求路径规则。以下是它们的具体作用及原理分析:
一、<servlet-class> 的作用
1. 指定核心前端控制器
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- 核心作用:明确声明当前 Servlet 的实现类为 SpringMVC 的前端控制器
DispatcherServlet。
DispatcherServlet是 SpringMVC 的入口,负责协调处理所有 Web 请求,包括请求分发、视图解析、异常处理等核心流程。 - 本质:
DispatcherServlet是HttpServlet的子类,遵循 Servlet 规范,通过重写doGet/doPost等方法实现对 SpringMVC 流程的控制。
2. 集成 SpringMVC 生态
- 该类会自动加载 SpringMVC 的配置(如
springmvc-servlet.xml),初始化处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)等组件,形成完整的请求处理链条。
二、<url-pattern> 的作用
1. 定义请求拦截规则
<url-pattern>/</url-pattern>
- 核心作用:指定哪些请求会被
DispatcherServlet处理。
常见取值有/*、/、*.do等,不同取值影响请求的拦截范围。
2. 不同取值的区别
| 取值 | 拦截范围 | 适用场景 |
|---|---|---|
/* | 拦截 所有请求(包括静态资源 .html/.js/.css、JSP 页面请求等)。 | 不推荐:会导致 JSP 页面无法正常访问(因 JSP 本质是 Servlet,需由 Tomcat 内置 Servlet 处理)。 |
/ | 拦截 除 .jsp 以外的所有请求(静态资源、RESTful 接口等)。 | 推荐配置:JSP 页面由 Tomcat 默认 Servlet 处理,避免与 DispatcherServlet 冲突。 |
*.do | 拦截 以 .do 结尾的请求(如 /user.do)。 | 传统 MVC 风格,通过后缀区分动态请求,目前逐渐被 RESTful 无后缀风格取代。 |
3. 为什么推荐使用 <url-pattern>/</url-pattern>?
- 避免 JSP 拦截问题:
当请求为 JSP 页面(如/index.jsp)时,Tomcat 会使用内置的JspServlet处理。若使用/*,DispatcherServlet会优先拦截 JSP 请求,导致 404 错误。
使用/时,DispatcherServlet仅拦截 非 JSP 请求(JSP 的扩展名.jsp会被 Tomcat 直接处理),确保静态页面和动态接口的正常分发。 - 支持 RESTful 风格:
无后缀的 URL(如/user/1)更简洁,符合现代 Web 开发规范,且无需额外配置后缀映射。
三、请求处理流程与配置的关系
- 请求到达 Tomcat:
浏览器发送请求(如/user/list或/index.jsp)。 - Tomcat 匹配 Servlet:
- 若请求是 JSP(
.jsp结尾):由 Tomcat 内置的JspServlet处理,直接返回页面。 - 若请求是其他路径(如
/user/list):根据<url-pattern>/</url-pattern>,由DispatcherServlet接管。
- 若请求是 JSP(
- DispatcherServlet 处理流程:
- 通过处理器映射(
HandlerMapping)找到对应的控制器(@Controller方法)。 - 调用控制器处理请求,返回模型数据和视图名。
- 视图解析器(
ViewResolver)解析视图,生成最终响应(如 HTML 页面或 JSON 数据)。
- 通过处理器映射(
四、最佳实践
配置 <url-pattern>/</url-pattern>:
这是 SpringMVC 推荐的现代配置方式,兼容 RESTful 接口和静态资源,同时避免与 JSP 冲突(若项目使用 JSP 作为视图)。
静态资源处理:
若项目包含静态资源(如 CSS/JS/ 图片),需在 SpringMVC 配置中额外声明静态资源映射,避免被 DispatcherServlet 拦截:
// WebMvcConfigurer 配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
无 JSP 项目(如前后端分离):
若项目完全使用 JSON 响应(无 JSP 视图),可安全使用 /*,但需确保静态资源路径正确映射。
总结
<servlet-class>:指定 SpringMVC 的核心前端控制器DispatcherServlet,启动请求处理的整个流程。<url-pattern>/</url-pattern>:精准拦截非 JSP 请求,确保动态接口由 SpringMVC 处理,静态资源和 JSP 由 Tomcat 内置机制处理,是现代 Web 开发的最佳实践配置。
通过合理配置这两个项,可实现请求的高效分发,避免资源访问冲突,提升项目的可维护性。
请求处理流程更加详细点
以下是 SpringMVC 请求处理流程 的详细解析,从客户端发送请求到服务器返回响应的完整链路,结合核心组件的协作机制展开说明:
一、请求处理的核心流程(12 个关键步骤)
1. 浏览器发送请求到 Tomcat
- 用户在浏览器输入 URL(如
http://localhost:8080/app/user/list),请求通过网络到达 Tomcat 服务器。 - Tomcat 根据
web.xml中的<url-pattern>匹配处理该请求的 Servlet:- 若请求是 JSP 页面(如
.jsp结尾):由 Tomcat 内置的JspServlet处理(直接跳过 SpringMVC)。 - 若请求是 非 JSP 请求(如
/user/list):根据配置url-pattern>/</url-pattern>,由DispatcherServlet接管。
- 若请求是 JSP 页面(如
2. DispatcherServlet 接收请求并初始化上下文
DispatcherServlet作为前端控制器,是请求处理的入口,负责协调所有后续组件。- 它会加载 SpringMVC 配置文件(如
springmvc-servlet.xml或通过 Java 配置类),初始化以下核心组件(在第一次请求时完成初始化):- 处理器映射器(HandlerMapping):将请求 URL 映射到具体的控制器(
@Controller方法)。 - 处理器适配器(HandlerAdapter):适配不同类型的控制器(如
@RequestMapping方法、HttpRequestHandler等)。 - 视图解析器(ViewResolver):将逻辑视图名解析为具体的视图对象(如 JSP、Thymeleaf 模板、JSON 视图等)。
- 其他组件:如异常处理器(
HandlerExceptionResolver)、文件上传解析器(MultipartResolver)等。
- 处理器映射器(HandlerMapping):将请求 URL 映射到具体的控制器(
3. 处理器映射器(HandlerMapping)查找 Handler
- 目标:根据请求 URL 找到对应的 处理器(Handler),即处理该请求的控制器方法。
- 工作机制:
- 遍历所有已注册的
HandlerMapping(如RequestMappingHandlerMapping),尝试匹配请求 URL。 - 例如,对于 URL
/user/list,若存在@RequestMapping("/user/list")注解的方法,则返回一个HandlerExecutionChain对象,包含:- Handler:具体的控制器方法(如
UserController.list())。 - 拦截器链(Interceptor List):请求处理前后执行的拦截器(如
HandlerInterceptor的preHandle/postHandle方法)。
- Handler:具体的控制器方法(如
- 遍历所有已注册的
- 输出:返回
HandlerExecutionChain,若未找到匹配的 Handler,则抛出404 Not Found异常。
4. 处理器适配器(HandlerAdapter)调用 Handler
- 目标:适配不同类型的 Handler,确保统一调用方式。
- 工作机制:
DispatcherServlet根据 Handler 的类型选择对应的HandlerAdapter(如RequestMappingHandlerAdapter处理@Controller方法)。- 调用
HandlerAdapter.handle()方法,执行以下操作:- 预处理:解析请求参数(如
@RequestParam、@RequestBody),绑定到控制器方法的参数列表。 - 调用控制器:执行具体的控制器方法,返回
ModelAndView(或void、String、ResponseEntity等类型,由适配器转换为ModelAndView)。
ModelAndView包含:- Model:视图渲染所需的数据(如
Map<String, Object>)。 - ViewName:逻辑视图名(如
user/list)或直接是View对象(如InternalResourceView对应 JSP)。
- Model:视图渲染所需的数据(如
- 预处理:解析请求参数(如
5. 拦截器前置处理(preHandle)
- 在调用控制器方法 之前,拦截器链中的所有拦截器依次执行
preHandle()方法:- 可用于权限校验、日志记录、请求参数修改等。
- 若某拦截器返回
false,则中断后续流程,直接执行afterCompletion()(不会调用控制器和后续拦截器)。
6. 执行控制器(@Controller 方法)
- 控制器方法被调用,处理业务逻辑:
- 接收请求参数(通过
@RequestParam、@PathVariable等注解)。 - 调用服务层、持久层获取数据。
- 返回结果:
- 若返回
String:表示逻辑视图名(如success),结合视图解析器生成最终视图。 - 若返回
ModelAndView:直接包含视图名和模型数据。 - 若返回
void:需通过HttpServletResponse直接输出响应(如 RESTful 接口返回 JSON 时,需配合@ResponseBody注解)。
- 若返回
- 接收请求参数(通过
7. 拦截器后置处理(postHandle)
- 在控制器方法 执行之后、视图渲染之前,拦截器链逆序执行
postHandle()方法:- 可用于修改模型数据(
ModelAndView中的Model)、补充响应头信息等。
- 可用于修改模型数据(
8. 视图解析器(ViewResolver)解析视图
- 目标:将逻辑视图名(如
user/list)转换为具体的View对象。 - 工作机制:
- 遍历所有已注册的
ViewResolver(如InternalResourceViewResolver处理 JSP)。 - 例如,配置
prefix="/WEB-INF/views/"和suffix=".jsp",则视图名user/list会被解析为:java
View = new InternalResourceView("/WEB-INF/views/user/list.jsp");
- 遍历所有已注册的
- 支持的视图类型:JSP、Thymeleaf、Freemarker、JSON(如
MappingJackson2JsonView)、XML 等。
9. 视图渲染(View.render ())
- 目标:将模型数据填充到视图中,生成最终的响应内容(如 HTML、JSON、XML 等)。
- 工作机制:
View对象调用render(Model, request, response)方法:- 对于 JSP 视图:将
Model数据放入request或session作用域,转发到 JSP 页面,JSP 通过 EL 表达式或标签库获取数据并渲染。 - 对于 JSON 视图(配合
@ResponseBody):直接将模型数据序列化为 JSON 格式,写入HttpServletResponse的输出流。
- 对于 JSP 视图:将
- 注意:若控制器方法返回
@ResponseBody或ResponseEntity,则跳过视图解析器,直接由HttpMessageConverter(如MappingJackson2HttpMessageConverter)处理响应数据。
10. 拦截器完成处理(afterCompletion)
- 在视图渲染 完成后,拦截器链逆序执行
afterCompletion()方法:- 用于资源清理(如关闭数据库连接、记录耗时日志等),无论请求是否成功都会执行。
11. DispatcherServlet 返回响应
- 生成的响应内容(如 HTML 页面、JSON 数据)通过
HttpServletResponse发送回浏览器。
12. 浏览器解析响应并展示
- 浏览器接收响应,解析 HTML/JSON 等内容,渲染页面或处理数据(如 AJAX 请求的回调函数)。
二、核心组件的协作关系图
浏览器请求 → Tomcat → DispatcherServlet↓[1] 处理器映射器(HandlerMapping) ↓ (返回 HandlerExecutionChain:Handler + 拦截器链)[2] 处理器适配器(HandlerAdapter) ↓ (调用 Controller 方法,返回 ModelAndView)[3] 视图解析器(ViewResolver) ↓ (解析为 View 对象)[4] 视图渲染(View.render()) ↓响应返回浏览器
三、关键组件的作用与扩展点
1. 处理器映射器(HandlerMapping)
- 默认实现:
RequestMappingHandlerMapping:处理@RequestMapping注解的控制器。BeanNameUrlHandlerMapping:按 Bean 名称映射 URL(较少使用)。
- 扩展:自定义
HandlerMapping,实现特殊的 URL 匹配逻辑(如基于数据库的动态路由)。
2. 处理器适配器(HandlerAdapter)
- 默认实现:
RequestMappingHandlerAdapter:支持@Controller注解的方法(最常用)。SimpleControllerHandlerAdapter:支持实现Controller接口的控制器(传统方式)。
- 扩展:自定义
HandlerAdapter,支持非标准的控制器类型(如自定义函数式处理器)。
3. 视图解析器(ViewResolver)
- 默认实现:
InternalResourceViewResolver:解析 JSP 视图(转发到WEB-INF目录下的页面)。ThymeleafViewResolver:解析 Thymeleaf 模板(需集成 Thymeleaf 依赖)。
- 扩展:自定义
ViewResolver,支持自定义视图类型(如 Markdown 视图、Excel 导出视图)。
4. 拦截器(Interceptor)
- 作用:在请求处理的 前、中、后 阶段插入自定义逻辑,实现:
- 登录校验(
preHandle)。 - 性能监控(记录方法执行时间)。
- 响应数据包装(统一添加响应头)。
- 登录校验(
- 实现:自定义类实现
HandlerInterceptor接口,重写preHandle/postHandle/afterCompletion方法,并通过WebMvcConfigurer.addInterceptors()注册。
四、典型场景处理细节
1. RESTful 接口返回 JSON(无视图渲染)
- 控制器方法添加
@ResponseBody注解(或使用@RestController组合注解):java
@GetMapping("/api/user/{id}") @ResponseBody public User getUser(@PathVariable Long id) {return userService.getUser(id); // 直接返回 Java 对象 } - 流程变化:
- 跳过视图解析器和视图渲染阶段。
- 由
HttpMessageConverter(如MappingJackson2HttpMessageConverter)将 Java 对象序列化为 JSON,写入响应体。
2. 文件上传处理
- 配置
MultipartResolver(如CommonsMultipartResolver):xml
<!-- web.xml 或 Java 配置 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="maxUploadSize" value="10485760" /> <!-- 10MB --> </bean> - 控制器方法接收
MultipartFile参数:java
@PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file) {// 处理文件上传逻辑return "uploadSuccess"; } - 流程变化:在
DispatcherServlet处理请求前,MultipartResolver会将HttpServletRequest包装为MultipartHttpServletRequest,解析文件数据。
3. 异常处理
- 配置
HandlerExceptionResolver(如SimpleMappingExceptionResolver或@ControllerAdvice):- 全局捕获控制器抛出的异常,返回自定义错误页面或 JSON 响应。
- 流程变化:若控制器方法抛出异常,会跳过视图渲染,直接由异常处理器处理,返回对应的错误视图或响应。
五、总结:SpringMVC 设计的核心思想
- 模块化设计:通过
DispatcherServlet统一调度,各组件(映射器、适配器、解析器)分工明确,可独立扩展。 - 约定大于配置:默认组件支持大多数场景,仅需少量配置即可快速启动(如自动装配
RequestMappingHandlerMapping)。 - 兼容性:支持多种控制器风格(注解式、接口式)和视图技术(JSP、模板引擎、REST 响应),适配不同项目需求。
理解这一流程后,开发者可更高效地调试问题(如 404 错误可能是 Handler 未匹配,500 错误可能是视图解析失败),并利用扩展点实现个性化需求(如自定义拦截器、视图解析器)。
