🔍 Spring Boot Actuator 源码阅读指南:从 @Endpoint 和 @ReadOperation 开始
要深入理解 Spring Boot Actuator 的工作原理,最好的方式是从其核心注解入手,逐步追踪源码执行流程。本文将带你从 @Endpoint、@ReadOperation 等关键注解出发,层层剖析 Actuator 的底层机制。
一、核心注解概览
Actuator 的扩展能力依赖于一组核心注解:
| 注解 | 作用 |
|---|---|
@Endpoint | 定义一个通用端点(支持 JMX + HTTP) |
@WebEndpoint | 仅支持 HTTP 的端点 |
@JmxEndpoint | 仅支持 JMX 的端点 |
@ReadOperation | 标记一个读取操作(GET) |
@WriteOperation | 标记一个写入操作(POST/PUT) |
@DeleteOperation | 标记一个删除操作(DELETE) |
@Selector | 用于从路径中提取变量 |
我们以最常用的 @Endpoint + @ReadOperation 为例,展开源码分析。
二、从 @Endpoint 注解开始
1. @Endpoint 注解定义
源码位置:org.springframework.boot.actuate.endpoint.annotation.Endpoint
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Endpoint {
String id();
boolean enableByDefault() default true;
}
id():端点的唯一标识,如health、info,最终映射为/actuator/{id}enableByDefault:是否默认启用
💡 示例:
@Component @Endpoint(id = "custom") public class CustomEndpoint { ... }
三、端点是如何被发现和注册的?
1. EndpointDiscoverer —— 端点发现器
核心类:EndpointDiscoverer<T extends AbstractEndpoint<?>>
- 负责扫描所有带有
@Endpoint、@WebEndpoint等注解的 Bean - 创建对应的
Operation(读、写、删) - 构建
ExposableEndpoint实例
关键方法:discoverEndpoints()
protected Collection<T> discoverEndpoints() {
Collection<T> endpoints = new ArrayList<>();
for (BeanDefinition candidate : getCandidateBeanDefinitions()) {
Class<?> beanType = getBeanType(candidate);
T endpoint = createEndpoint(beanType);
if (endpoint != null) {
endpoints.add(endpoint);
}
}
return endpoints;
}
它会从 Spring 容器中查找所有候选 Bean,并通过反射解析注解。
2. Operation 的创建:@ReadOperation 是如何生效的?
当发现一个 @Endpoint 类时,会调用 createEndpointOperations() 解析其中的方法。
核心类:AnnotationEndpointDiscoverer
它会遍历类中的方法,查找带有 @ReadOperation、@WriteOperation 等注解的方法。
private Collection<Operation> getOperations(Object endpoint, Class<?> type) {
Collection<Operation> operations = new ArrayList<>();
for (Method method : ReflectionUtils.getDeclaredMethods(type)) {
if (isReadOperation(method)) {
operations.add(createReadOperation(endpoint, method));
} else if (isWriteOperation(method)) {
operations.add(createWriteOperation(endpoint, method));
}
// ...
}
return operations;
}
判断是否为 @ReadOperation:
private boolean isReadOperation(Method method) {
return MergedAnnotations.from(method).isPresent(ReadOperation.class);
}
四、@ReadOperation 注解详解
1. 注解定义
源码:org.springframework.boot.actuate.endpoint.annotation.ReadOperation
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadOperation {
}
它是一个标记注解(Marker Annotation),本身不包含属性,仅用于标识。
2. createReadOperation() 创建 WebOperation
以 Web 端点为例,最终会创建 WebOperation 实例,封装了:
- 目标方法(Method)
- 参数解析器(
OperationParameterMapper) - 返回值处理器
- 路径变量(
@Selector)
示例:自定义端点
@Component
@Endpoint(id = "feature")
public class FeatureEndpoint {
private Map<String, Boolean> features = new HashMap<>();
@ReadOperation
public Map<String, Boolean> getStatus() {
return features;
}
@WriteOperation
public void setFeature(@Selector String name, boolean enabled) {
features.put(name, enabled);
}
}
五、HTTP 端点是如何映射到 Web 请求的?
1. WebEndpointServletConfiguration 自动配置
在 spring-boot-autoconfigure 中,WebEndpointAutoConfiguration 会导入 WebEndpointServletConfiguration。
它会创建:
WebEndpointsSupplier:提供所有可暴露的 Web 端点WebEndpointDiscoverer:专门发现@WebEndpoint和@Endpoint(支持 HTTP 的)
2. WebEndpointResponseMapper 处理返回值
将 @ReadOperation 方法的返回值转换为 HTTP 响应体(JSON)。
3. ControllerEndpointHandlerMapping —— Spring MVC 集成
关键类:ControllerEndpointHandlerMapping
它实现了 HandlerMapping,将 /actuator/{id} 路径映射到对应的 @Endpoint 方法。
工作流程:
- 启动时,
WebEndpointDiscoverer扫描所有@EndpointBean - 构建
ExposableWebEndpoint实例,包含WebOperation ControllerEndpointHandlerMapping注册这些端点为 Spring MVC 的 Handler- 当请求
/actuator/feature时,调用FeatureEndpoint.getStatus()方法 - 返回值通过
WebEndpointResponseMapper序列化为 JSON
六、深入 @Endpoint 与 @RestControllerEndpoint 的区别
| 类型 | 注解 | 底层机制 | 适用场景 |
|---|---|---|---|
| 通用端点 | @Endpoint + @ReadOperation | 基于 OperationInvoker 反射调用 | 推荐,轻量、跨 JMX/HTTP |
| Web 专用 | @WebEndpoint | 仅 HTTP,类似 @RestController | 简单场景 |
| 完全控制 | @RestControllerEndpoint | 完整的 @RestController 语义 | 需要复杂逻辑、异常处理 |
💡
@RestControllerEndpoint本质是一个带有@Controller的类,由ControllerEndpointHandlerMapping注册。
七、源码调用链路总结(以 /actuator/custom 为例)
1. Application Start
└── WebEndpointDiscoverer.discoverEndpoints()
└── 扫描 @Endpoint(id="custom") 类
└── 解析 @ReadOperation 方法 → 创建 WebOperation
2. ControllerEndpointHandlerMapping 注册端点
└── 将 /actuator/custom 映射到 CustomEndpoint.getStatus()
3. HTTP 请求到达
GET /actuator/custom
└── DispatcherServlet → HandlerMapping 匹配
└── 调用 OperationInvoker.invoke()
└── 反射执行 getStatus() 方法
└── WebEndpointResponseMapper 序列化返回 JSON
八、关键源码类图(简化)
+---------------------+
| @Endpoint |
| @ReadOperation |
+----------+----------+
|
v
+----------------------------------+
| EndpointDiscoverer |
| - discoverEndpoints() |
| - createOperation() |
+----------------+-----------------+
|
v
+------------------------+-------------------------+
| ExposableWebEndpoint | ExposableJmxEndpoint |
| - getOperations() | - getOperations() |
+------------------------+-------------------------+
|
v
+----------------------------------------------------------+
| ControllerEndpointHandlerMapping |
| - register endpoints as Spring MVC handlers |
| - handle /actuator/* requests |
+----------------------------------------------------------+
|
v
+----------------------------------+
| OperationInvoker |
| - invoke() via reflection |
+----------------------------------+
九、如何调试源码?
-
克隆 Spring Boot 源码
git clone https://github.com/spring-projects/spring-boot.git -
导入 IDE(推荐 IntelliJ IDEA)
-
设置断点
EndpointDiscoverer.discoverEndpoints()AnnotationEndpointDiscoverer.getOperations()ControllerEndpointHandlerMapping.getHandler()
-
运行你的应用,观察端点注册过程
十、总结:从注解到运行的完整理解
| 阶段 | 关键动作 |
|---|---|
| 1. 注解定义 | @Endpoint, @ReadOperation 提供元数据 |
| 2. 扫描发现 | EndpointDiscoverer 扫描并创建端点实例 |
| 3. 操作解析 | 解析 @ReadOperation 方法 → 创建 Operation |
| 4. 映射注册 | ControllerEndpointHandlerMapping 注册为 MVC 路由 |
| 5. 请求处理 | 反射调用方法,返回 JSON 响应 |
✅ 收获:
- 理解了
@Endpoint不是简单的 Controller,而是通过元数据驱动 + 反射调用实现的通用模型。 - 明白了为什么
@ReadOperation方法不需要@ResponseBody—— 因为 Actuator 内部已处理。 - 掌握了扩展自定义端点的底层原理,便于后续定制开发。

766

被折叠的 条评论
为什么被折叠?



