一、背景
jvm-sandbox-repeater提供了http-plugin的基本实现,通过拦截javax.servlet.http.HttpServlet#service 方法从而拿到http的请求和返回,本次我们将扩展该插件实现,支持springMVC path url的变量解析和提取
首先我们定义一个controller, 带路径变量的
@RequestMapping("/api/test")
@RestController
public class TestController {
@ResponseBody
@RequestMapping("testPathVariable/{v}")
public Result testPathVariable(@PathVariable String v) {
System.out.println(v);
return Result.buildSuccess("成功");
}
}
我们请求该url, 执行shell命令
curl 'http://127.0.0.1:8080/api/test/testPathVariable/111'
那么对于这种的, 流量采集之后的路径是 /api/test/testPathVariable/111, 其中111其实是变量
因为我们涉及到很多配置,需要以路径来作为标识,因此理想的效果应该如下, 也就是 /api/test/testPathVariable/{v}
至此,应该理解我想要达到的效果了;
二、springMVC 关键代码解析
springMvc启动的时候,会初始化handlerMappings, HandlerMapping 是一个非常重要的组件,它的作用是将URL请求映射到相应的处理程序上。具体来说,HandlerMapping 会根据URL请求的路径、请求参数等信息,确定需要执行哪个处理程序,并将该处理程序返回给 DispatcherServlet。然后 DispatcherServlet 再将请求分配给相应的处理程序,处理程序处理完请求后,将结果返回给 DispatcherServlet,DispatcherServlet 再将结果返回给客户端。
其中handlerMapping有多种实现,见截图
我们通过debug发现,DispatcherServlet#doDispatch 中会调用 org.springframework.web.servlet.DispatcherServlet#getHandler 来获取执行链,最终会执行到org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch里,其中有一段非常重要的逻辑跟每次业务有关系
再看 org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#extractMatchDetails(org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition, java.lang.String, javax.servlet.http.HttpServletRequest)
private void extractMatchDetails(
PathPatternsRequestCondition condition, String lookupPath, HttpServletRequest request) {
PathPattern bestPattern;
Map uriVariables;
if (condition.isEmptyPathMapping()) {
bestPattern = condition.getFirstPattern();
uriVariables = Collections.emptyMap();
}
else {
PathContainer path = ServletRequestPathUtils.getParsedRequestPath(request).pathWithinApplication();
bestPattern = condition.getFirstPattern();
PathPattern.PathMatchInfo result = bestPattern.matchAndExtract(path);
Assert.notNull(result, () ->
"Expected bestPattern: " + bestPattern + " to match lookupPath " + path);
uriVariables = result.getUriVariables();
request.setAttribute(MATRIX_VARIABLES_ATTRIBUTE, result.getMatrixVariables());
}
//这里将相关的urlPattern都放到请求的扩展属性里了
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern.getPatternString());
request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
}
至此,我们就可以发现,直接从request的扩展属性中提取相应信息即可;
三、http plugin修改
修改com.alibaba.jvm.sandbox.repater.plugin.http.wrapper.WrapperTransModel,新增如下方法
public String getMatchPattern() {
String matchPattern = (String) request.getAttribute("org.springframework.web.servlet.HandlerMapping.bestMatchingPattern");
return matchPattern;
}
public Map getUriVariables() {
Map res = new HashMap();
if (request.getAttribute("org.springframework.web.servlet.HandlerMapping.uriTemplateVariables")!=null) {
res.putAll((Map)request.getAttribute("org.springframework.web.servlet.HandlerMapping.uriTemplateVariables"));
}
return res;
}
修改
com.alibaba.jvm.sandbox.repater.plugin.http.HttpStandaloneListener#assembleHttpAttribute, 将刚才的2个属性放进去即可