概要

通过自定义注解 @ApiMethod 结合 Spring 拦截器 ApiHandlerMapping,实现对 /api/** 路径 POST 请求的动态路由拦截,将请求映射到指定业务服务的对应方法,无需编写大量 Controller 层代码,提升接口开发灵活性。

核心组件说明

1. 自定义注解 @ApiMethod

用于标记业务服务中需要对外暴露的 API 方法,通过注解值绑定 API 路径,支持运行时反射获取注解信息。

1
2
3
4
5
6
7
8
9
10
11
12
13

import java.lang.annotation.*;

/**
* API方法绑定注解
* 用于标记业务服务中可被/api/**路径调用的方法
*/
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,支持反射获取
@Target(ElementType.METHOD) // 仅作用于方法
public @interface ApiMethod {
/** 绑定的API子路径(如create、getuser) */
String value() default "";
}

2. 动态路由处理器 ApiHandlerMapping

核心拦截器,实现 Spring 的 HandlerMapping 接口,专门处理 /api/** 路径的 POST 请求,通过反射+注解匹配,将请求转发到对应业务服务方法,整体流程清晰、扩展性强。

核心处理流程

当 POST 请求 http://域名/api/模块名/方法名 到达时:

  1. ApiHandlerMapping.getHandler() 匹配 /api/ 前缀 + POST 方法,返回自定义处理器 ApiHandler

  2. ApiHandler.handleRequest() 执行核心逻辑:

    • 解析 URI:/api/user/create → 模块名 user、方法名 create

    • 读取 JSON 请求体:通过 Jackson 解析为通用 JsonNode 结构,兼容任意 JSON 格式入参

    • 调用 invokeService():根据模块名匹配 Spring 容器中的业务服务,根据注解匹配对应方法并执行

    • 统一封装响应结果(成功/失败)

读取请求体为 JsonNode

1
JsonNode requestBody = objectMapper.readTree(request.getInputStream());
  • 使用 Jackson 将整个 POST body 解析为通用 JSON 树结构
  • 无论传 {}、{“name”:”A”} 还是 [] 都能解析(非法 JSON 会抛 IOException)

invokeService 业务处理方式
根据模块名和方法名,从 Spring 容器中找到对应 Service,并调用带 @ApiMethod 注解的方法。

1
invokeService(String moduleName, String methodName, JsonNode args)

构造 Service Bean 名称

1
2
String serviceName = moduleName + "ApiService";
Object service = applicationContext.getBean(serviceName);

查找 @ApiMethod 注解, 没有注解时寻找内部方法名相同的接口

1
ApiMethod ann = getApiMethodAnnotation(method, serviceClass);

writeError 统一封装 错误提醒,往外丢出

1
private void writeError(HttpServletResponse response, String message, int status) throws IOException 

完整实现代码(优化排版+注释)

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
* API动态路由处理器
* 拦截/api/**的POST请求,动态路由到对应业务服务的@ApiMethod注解方法
* @Order(1) 保证优先于默认HandlerMapping执行
*/
@Order(1)
public class ApiHandlerMapping implements HandlerMapping, ApplicationContextAware {

// Spring容器上下文,用于获取业务服务Bean
private ApplicationContext applicationContext;
// Jackson JSON解析工具,全局单例
private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

/**
* 核心拦截逻辑:匹配/api/**的POST请求
*/
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) {
String uri = request.getRequestURI();
String method = request.getMethod();
System.out.println("📝 接收到请求:URI=" + uri + ", Method=" + method);

// 仅处理/api/前缀的POST请求,其他请求交给Spring默认处理器
if (uri.startsWith("/api/") && "POST".equalsIgnoreCase(method)) {
return new HandlerExecutionChain(new ApiHandler());
}
return null;
}

/**
* 内部请求处理器
* 负责解析请求、调用业务方法、封装响应
*/
class ApiHandler implements HttpRequestHandler {

@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
// 1. 解析API路径:/api/模块名/方法名 → 拆分模块和方法
String pathInfo = request.getRequestURI().substring("/api/".length());
String[] pathParts = pathInfo.split("/", 2);
if (pathParts.length != 2) {
writeError(response, "无效的API路径格式,正确格式:/api/模块名/方法名", 400);
return;
}
String moduleName = pathParts[0]; // 模块名(如user)
String methodName = pathParts[1]; // 方法名(如create)

// 2. 读取JSON请求体(兼容任意JSON格式,非法JSON会抛出IOException)
JsonNode requestBody = objectMapper.readTree(request.getInputStream());

// 3. 调用业务服务方法
Object businessResult = invokeService(moduleName, methodName, requestBody);

// 4. 封装成功响应
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
objectMapper.writeValue(
response.getWriter(),
Map.of("code", 200, "data", businessResult, "msg", "操作成功")
);

} catch (Exception e) {
// 5. 统一异常处理,封装错误响应
writeError(response, "接口调用失败:" + e.getMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}

/**
* 调用业务服务方法
* @param moduleName 模块名(对应服务Bean名:模块名 + ApiService)
* @param methodName API方法名(对应@ApiMethod注解值)
* @param args JSON请求参数
* @return 业务方法执行结果
* @throws Exception 执行异常
*/
private Object invokeService(String moduleName, String methodName, JsonNode args) throws Exception {
// 构造服务Bean名称(如user → userApiService)
String serviceBeanName = moduleName + "ApiService";
Object serviceBean = applicationContext.getBean(serviceBeanName);

// 防御性检查:服务Bean不存在则抛出异常
if (serviceBean == null) {
throw new IllegalArgumentException("未找到指定的业务服务:" + serviceBeanName);
}

Class<?> serviceClass = serviceBean.getClass();
// 遍历服务类所有方法,匹配@ApiMethod注解
for (Method method : serviceClass.getMethods()) {
// 穿透获取注解(当前方法→接口方法→父类方法)
ApiMethod apiMethodAnn = getApiMethodAnnotation(method, serviceClass);

// 注解值匹配则执行方法
if (apiMethodAnn != null && methodName.equals(apiMethodAnn.value())) {
method.setAccessible(true); // 允许访问私有/保护方法
try {
return method.invoke(serviceBean, args); // 执行方法并返回结果
} catch (InvocationTargetException e) {
// 解包目标异常,暴露真实业务异常信息
throw new Exception("方法执行失败:" + e.getTargetException().getMessage(), e.getTargetException());
} catch (IllegalAccessException e) {
throw new Exception("方法访问权限不足:" + method.getName(), e);
}
}
}
throw new IllegalArgumentException("服务[" + serviceBeanName + "]中未找到绑定@ApiMethod(\"" + methodName + "\")的方法");
}

/**
* 穿透获取ApiMethod注解(支持接口/父类注解继承)
* @param method 目标方法
* @param targetClass 目标类
* @return ApiMethod注解(无则返回null)
*/
private ApiMethod getApiMethodAnnotation(Method method, Class<?> targetClass) {
// 1. 优先获取当前方法的注解
ApiMethod annotation = method.getAnnotation(ApiMethod.class);
if (annotation != null) {
return annotation;
}

// 2. 获取实现接口中方法的注解
for (Class<?> interfaceClass : targetClass.getInterfaces()) {
try {
Method interfaceMethod = interfaceClass.getMethod(method.getName(), method.getParameterTypes());
annotation = interfaceMethod.getAnnotation(ApiMethod.class);
if (annotation != null) {
return annotation;
}
} catch (NoSuchMethodException e) {
continue; // 接口无此方法,跳过
}
}

// 3. 获取父类方法的注解(可选)
if (targetClass.getSuperclass() != null && targetClass.getSuperclass() != Object.class) {
try {
Method superClassMethod = targetClass.getSuperclass().getMethod(method.getName(), method.getParameterTypes());
annotation = superClassMethod.getAnnotation(ApiMethod.class);
if (annotation != null) {
return annotation;
}
} catch (NoSuchMethodException e) {
// 父类无此方法,忽略
}
}

return null;
}

/**
* 统一错误响应封装
* @param response 响应对象
* @param message 错误信息
* @param status HTTP状态码
* @throws IOException 写入响应异常
*/
private void writeError(HttpServletResponse response, String message, int status) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(status);
objectMapper.writeValue(
response.getWriter(),
Map.of("code", status, "error", message, "msg", "操作失败")
);
}
}
}

使用方式

在业务服务接口/实现类中添加 @ApiMethod 注解,注解值与 API 路径中的方法名对应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

import com.fasterxml.jackson.databind.JsonNode;

/**
* 用户业务API服务接口
* Bean名称:mcpApiService(模块名mcp + ApiService)
*/
public interface McpApiService {

/**
* 创建用户接口
* 对应API路径:/api/mcp/create
*/
@ApiMethod("create")
Object createUser(JsonNode args);

/**
* 查询用户接口
* 对应API路径:/api/mcp/getuser
*/
@ApiMethod("getuser")
Object selectUser(JsonNode args);
}

调用示例

请求地址:POST /api/mcp/create

请求体:

1
2
3
4
5

{
"username": "test",
"password": "123456"
}

响应示例:

1
2
3
4
5
6
7
8
9

{
"code": 200,
"data": {
"userId": 1001,
"username": "test"
},
"msg": "操作成功"
}
  • 无侵入式路由:无需编写 Controller,通过注解直接绑定业务方法与 API 路径,减少冗余代码;

  • 通用参数解析:基于 JsonNode 接收任意 JSON 格式参数,适配不同业务场景;

  • 注解穿透获取:支持接口、父类的注解继承,兼容不同编码风格的服务实现;

  • 统一响应封装:成功/失败响应格式标准化,异常信息解包暴露真实原因,便于排查问题;

  • 灵活扩展:可通过扩展 getApiMethodAnnotation 方法,支持更多注解匹配规则,或增加参数校验、权限控制等逻辑。