一些中间件的路由提取方法.md
2023-11-12 12:16:19

Spring

Spring 有个统一的调度入口:DispatcherServlet

1
org.springframework.web.servlet.DispatcherServlet#getHandler

然后通常就会进入到这里:

1
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

上面这个是抽象类,实现类应该是:

1
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

然后路由应该这个属性里(不一定全):

1
mappingRegistry.registry

Ver:3.2.0

如果是 3.2.0 版本,要使用 JDK17,那么想使用反射获取 URL 好像还要加上

1
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED

JDK9 以后加上了这个:

image-20231113030229061

同时,还需要先访问一次任意路由,这样DispatcherServlet 才会进行初始化:

image-20231113030600979

Tomcat

路由信息

1
org.apache.catalina.connector.CoyoteAdapter#postParseRequest

断点在这附近:

image-20231025180746432

一个提取路由的脚本:

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
Object[] o = ((((StandardService)((Connector)((CoyoteAdapter)this).connector).service).mapper).contextObjectToContextVersionMap).entrySet().toArray();
Object test;
for (int i = 0; i < o.length; i++) {
Mapper.ContextVersion o1 = (Mapper.ContextVersion) ((java.util.concurrent.ConcurrentHashMap.MapEntry)o[i]).getValue();

Mapper.MappedWrapper[] mappers = ((Mapper.MappedWrapper[])o1.exactWrappers;
for (int j = 0; j < mappers.length; j++) {
String write_str = o1.path;
write_str += mappers[j].name+"\t";
write_str+=mappers[j].object.getServletClass()+"\t";
new FileOutputStream("C:/route.txt",true).write((write_str+"\r\n").getBytes());
}

mappers = ((Mapper.MappedWrapper[])o1.wildcardWrappers;
for (int j = 0; j < mappers.length; j++) {
String write_str = o1.path+"/";
write_str += mappers[j].name+"/*\t";
write_str+=mappers[j].object.getServletClass()+"\t";
new FileOutputStream("C:/route.txt",true).write((write_str+"\r\n").getBytes());
}

mappers = ((Mapper.MappedWrapper[])o1.extensionWrappers;
for (int j = 0; j < mappers.length; j++) {
String write_str = o1.path+"/";
write_str +="*."+ mappers[j].name+"\t";
write_str+=mappers[j].object.getServletClass()+"\t";
new FileOutputStream("C:/route.txt",true).write((write_str+"\r\n").getBytes());
}


}

Resin

resin 是怎么拿到路由的呢:

首先会在这里:

1
com.caucho.server.http.HttpRequest#handleRequest

image-20230927182249384

在此处根据 uri 获取 invocation

1
com.caucho.server.http.AbstractHttpRequest#getInvocation

image-20230927182418309

首先上面是获取缓存,应该是判断这个路由之前是否被访问过,如果被访问过就会在如下这个 cache里:

image-20230927182500535

没有的话就会进入到 buildInvocation,这里进入的是一个 defaulthost,就是默认的,应该会有别的情况,但是没遇到,就先不记了,一路跟到这里:

1
com.caucho.server.webapp.WebAppContainer#buildInvocation

image-20230927182727912

1
com.caucho.server.webapp.WebAppContainer#getWebAppController

应该是根据 URI 找控制器:

image-20230927182753505

然后再进一层:

1
com.caucho.server.webapp.WebAppContainer#findEntryByURI

image-20230927183413838

首先根据 uricache 中获取,如果没有的话就再进入 findByURIImpl,这里写个简单的代码,可以把它导出来:

1
2
3
4
5
6
Object[] xxx= Arrays.stream(this._uriToAppCache._entries).toArray();
for (int i = 0; i < xxx.length; i++) {
if (xxx[i]!=null){
new FileOutputStream("C:/uritocache2.txt",true).write(((String)xxx[i]._key+"\n").getBytes());
}
}

然后就是 findByURIImpl

1
com.caucho.server.webapp.WebAppContainer#findByURIImpl

image-20230927183649614

可以看到这里又从 cache 里拿一次,是因为上面最后对 uri 做了一次 normalize,然后如果还找不到就去 appDeploy 里找(这里应该是部署的应用)。

这个 findController也是主要从两个地方找:

1
2
3
com.caucho.env.deploy.DeployContainer#findController ->
com.caucho.env.deploy.DeployContainer#findDeployedController
com.caucho.env.deploy.DeployContainer#generateController

主要还是看第二个 generateController

image-20230927184021121

如果这里还找不到的话,回到 findByURIImpl

image-20230927184218368

此处会往下递归,找到最后一个 / 做截断,然后往下找,可能需求就是要找类似 /*这样的东西。

resin 路由位置

不过上面的看不太懂,但是我找到一处可以拿到一些 url 的地方(这个地方要在找一个新的 uri 的时候会调用到,所以记得改一下 uri ):

1
com.caucho.server.dispatch.ServletMapper#mapServlet

image-20230928121026509

Struts

1
2
测试版本:
2.5.10.1

断点可以下在:

1
com.opensymphony.xwork2.config.impl.DefaultConfiguration.RuntimeConfigurationImpl#findActionConfigInNamespace
Prev
2023-11-12 12:16:19
Next