0%

Spring MVC HandlerAdapter 详解:处理器适配器的原理与实战

HandlerAdapter(处理器适配器)是 Spring MVC 中连接 DispatcherServletHandler(处理器,如 Controller) 的关键组件。由于 Spring MVC 支持多种 Handler 实现方式(如实现 Controller 接口、HttpRequestHandler 接口、注解驱动的 @Controller 方法),DispatcherServlet 无法直接调用不同类型的 Handler,需通过 HandlerAdapter 进行 “适配”——将统一的调用逻辑转换为具体 Handler 的执行方式,彻底解耦前端控制器与处理器。从 “核心定义→适配器模式→实现类解析→核心流程源码” 四个维度,彻底讲透 HandlerAdapter 的工作机制。

HandlerAdapter 核心定义与设计思想

1. 核心接口与方法

HandlerAdapter 接口定义了所有适配器的通用行为,共 3 个核心方法,覆盖 “适配判断→执行逻辑→资源时间戳” 三大功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface HandlerAdapter {
/**
* 1. 判断当前适配器是否支持指定的 Handler
* @param handler 待适配的处理器(如 Controller 实例、HandlerMethod)
* @return true:支持,可执行;false:不支持
*/
boolean supports(Object handler);

/**
* 2. 执行 Handler 的核心逻辑,返回 ModelAndView(无视图则返回 null)
* @param request HTTP 请求对象
* @param response HTTP 响应对象
* @param handler 待执行的处理器
* @return ModelAndView:封装模型数据与视图名;null:无需视图渲染(如 @ResponseBody 接口)
*/
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

/**
* 3. 获取 Handler 对应资源的最后修改时间(用于缓存控制,如 Last-Modified 请求头)
* @return 时间戳(毫秒);-1:不支持缓存控制
*/
long getLastModified(HttpServletRequest request, Object handler);
}

2. 设计思想:适配器模式

HandlerAdapter 是 适配器模式 的典型应用,核心目标是 “将不同类型的 Handler 统一适配为 DispatcherServlet 可调用的接口”。

阅读全文 »

Spring MVC HandlerMapping 详解:处理器映射器的原理与实战

HandlerMapping 是 Spring MVC 中连接 “HTTP 请求” 与 “处理器(Handler,即 Controller)” 的核心组件,其核心职责是 根据请求的 URL 或其他属性,找到对应的 Handler 及拦截器(Interceptor),并封装为 HandlerExecutionChain 对象返回给 DispatcherServlet。从 “核心定义→实现类对比→配置方式→源码解析” 四个维度,彻底讲透 HandlerMapping 的工作机制。

HandlerMapping 核心定义与作用

HandlerMapping 是一个接口,定义了所有处理器映射器的核心行为,其本质是 “请求→Handler” 的映射器,相当于 MVC 中的 “路由表”。

1. 核心接口方法

1
2
3
4
5
6
7
8
public interface HandlerMapping {
/**
* 根据 HTTP 请求,查找对应的 Handler 及拦截器,封装为 HandlerExecutionChain
* @param request 当前 HTTP 请求
* @return HandlerExecutionChain(包含 Handler + 拦截器),无匹配则返回 null
*/
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
  • 返回值 HandlerExecutionChain:包含两部分核心内容:
    1. Handler:具体的业务处理器(如 @Controller 类的方法,封装为 HandlerMethod);
    2. HandlerInterceptor 数组:与当前请求匹配的拦截器,按配置顺序存储。

2. 核心数据结构:handlerMap

几乎所有 HandlerMapping 实现类内部都会维护一个 Map<String, Object> 类型的 handlerMap,用于存储 “URL 路径→Handler” 的映射关系,确保请求到来时能快速查找。例如:

1
2
// 以 AbstractUrlHandlerMapping 为例,维护 URL 到 Handler 的映射
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
  • Key:URL 路径(如 /user/list/api/login);
  • Value:Handler 对象(如 UserController 实例、HandlerMethod 实例)。

HandlerMapping 主要实现类与适用场景

HanderMapping继承关系

Spring MVC 提供了多种 HandlerMapping 实现,支持不同的映射策略(如按 Bean 名、按类名、按注解),以下是常用实现类的对比:

阅读全文 »

Spring MVC DispatcherServlet 详解:前端控制器的配置、源码与核心流程

DispatcherServlet 是 Spring MVC 的前端控制器(Front Controller),作为整个框架的入口,统一接收并调度所有 HTTP 请求,协调 HandlerMapping、HandlerAdapter、ViewResolver 等组件完成请求处理与响应生成。其核心价值在于解耦组件依赖,通过 “统一入口 + 策略模式” 让各组件专注于单一职责(如 URL 映射、参数绑定、视图渲染)。从 “配置方式→初始化流程→请求处理流程→核心组件协作” 四个维度,彻底解析 DispatcherServlet 的工作机制。

DispatcherServlet 核心定位与配置

DispatcherServlet 本质是一个标准的 Servlet(继承 HttpServlet),需在 Web 应用中配置才能生效。根据 Servlet 版本不同,分为 web.xml 配置(Servlet 2.5 及以下)和 注解配置(Servlet 3.0+)两种方式。

1. 传统配置:web.xml 方式

通过 web.xml 声明 DispatcherServlet,指定配置文件路径、映射规则及静态资源处理策略,是早期项目的主流配置方式。

完整 web.xml 配置示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="4.0">

<!-- 1. 处理静态资源(避免 DispatcherServlet 拦截 .html 文件) -->
<servlet-mapping>
<servlet-name>default</servlet-name> <!-- 容器默认 Servlet,负责处理静态资源 -->
<url-pattern>*.html</url-pattern> <!-- .html 后缀的请求交给默认 Servlet -->
</servlet-mapping>

<!-- 2. 配置 DispatcherServlet(Spring MVC 前端控制器) -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 初始化参数:指定 Spring MVC 配置文件路径(默认:/WEB-INF/<servlet-name>-servlet.xml) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value> <!-- 类路径下的配置文件 -->
</init-param>

<!-- 启动顺序:值为非负整数,容器启动时初始化(值越小优先级越高) -->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- 3. 配置 DispatcherServlet 映射规则 -->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 映射路径:/ 表示拦截所有未被其他 Servlet 匹配的请求(除 .jsp 外) -->
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 4. 可选:配置 ContextLoaderListener(创建根容器,用于业务层 Bean) -->
<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>
</web-app>
关键配置解析
配置项 作用描述
init-param: contextConfigLocation 指定 Spring MVC 配置文件路径(如 classpath:springmvc.xml),若不配置则默认加载 /WEB-INF/DispatcherServlet-servlet.xml
load-on-startup: 1 容器启动时初始化 DispatcherServlet,避免首次请求时的初始化延迟;值越小,初始化优先级越高
url-pattern: / 拦截所有未被其他 Servlet 匹配的请求(如 /user/list/api/login),但不拦截 .jsp(由 Tomcat 的 JspServlet 处理)
静态资源映射(default Servlet) .html 等静态资源交给容器默认 Servlet 处理,避免 DispatcherServlet 拦截无法解析

2. 现代配置:Servlet 3.0+ 注解方式

Servlet 3.0 规范支持 “无 XML 配置”,Spring 通过 WebApplicationInitializer 接口实现自动注册。只需继承 AbstractAnnotationConfigDispatcherServletInitializer 并实现核心方法,即可替代 web.xml

注解配置示例
阅读全文 »

Memcached 过期与删除机制:Lazy Expiration 与 LRU 策略详解

Memcached 作为内存缓存系统,其过期数据处理和内存回收机制直接影响缓存有效性和资源利用率。与传统数据库的主动过期检查不同,Memcached 采用懒惰删除(Lazy Expiration)LRU(最近最少使用) 策略,以最小的性能开销实现内存管理。本文深入解析这两种机制的原理、优缺点及实际表现。

Lazy Expiration(延迟过期):不主动检查,按需验证

核心原理

Memcached 不会主动监控数据是否过期,而是在用户尝试获取数据时才检查其过期时间:

  1. 当客户端执行 get <key> 时,Memcached 首先判断该键是否存在。
  2. 若存在,检查其时间戳是否过期(当前时间 > 过期时间)。
  3. 若已过期,返回 “键不存在”(END),并将该键标记为无效,从 curr_items 统计中移除。
  4. 若未过期,返回数据,并更新其 “最近使用时间”(用于 LRU 策略)。

优点

  • 节省 CPU 资源:无需维护定时器或线程监控所有键的过期状态,避免频繁检查带来的性能损耗。
  • 简单高效:过期检查与数据访问绑定,仅在必要时执行,适合高并发场景。

缺点

  • 内存占用延迟释放:过期数据可能长时间驻留内存(直到被访问或被 LRU 淘汰),导致短期内存利用率下降。
  • 统计数据延迟更新stats 命令中的 curr_items 会包含已过期但未被访问的键,直至其被主动获取或淘汰。

示例

  1. 存储一个 10 秒后过期的键:

阅读全文 »

Memcached 内存管理:Slab Allocator 机制详解

Memcached 作为高性能内存缓存系统,其内存管理机制直接影响性能和资源利用率。为解决传统内存分配(malloc/free)导致的内存碎片化问题,Memcached 采用了 Slab Allocator 机制,通过预分配固定大小的内存块,实现高效的内存复用。本文深入解析这一机制的原理、优缺点及优化策略。

内存碎片化问题与 Slab 机制的诞生

传统内存分配方式(如 C 语言的 mallocfree)在频繁分配和释放不同大小的内存块时,会产生大量内存碎片

  • 碎片是指无法被利用的空闲内存(如多次分配 / 释放后,内存中存在大量小于申请大小的零散空间)。
  • 碎片积累会导致内存利用率下降,甚至出现 “内存充足但无法分配连续空间” 的矛盾。

为解决这一问题,Memcached 引入 Slab Allocator 机制,核心思想是:按固定大小预分配内存块,根据数据大小匹配最合适的块,避免碎片化

Slab Allocator 核心组件与工作流程

核心组件

  • Slab(内存 slab 区):Memcached 将内存划分为多个 Slab,每个 Slab 对应一种固定大小的内存块(chunk)。
  • Page(页):每个 Slab 由多个 Page 组成,Page 是内存分配的基本单位(默认大小为 1MB)。
  • Chunk(块):每个 Page 被分割为多个大小相同的 Chunk,Chunk 是存储数据的最小单元(如 48B、60B、75B 等)。
  • Slab Class(Slab 类别):具有相同 Chunk 大小的 Slab 集合,每个类别用 class ID 标识(如 class 1 对应 48B Chunk,class 2 对应 60B Chunk 等)。

工作流程

阅读全文 »