vscjava.vscode-java-debug 0.18.0的新特性!

微软为VSCode开发了一个Java调试器 Debugger for Java。之前用这个很不爽,还和微软的人吐槽过VSCode在debug java的时候,只能看到HashMap等java自带数据结构的物理视图,比如一个HashMap,在 0.17.0 版本下debug时,是这样的:

HashMap里面有很多实现的细节,但是一般在debug的时候,我们比较关注的是这个HashMap里面存储了哪些东西等,而不是这种具体实现的细节。

然后0.18.0就实现了HashMap的逻辑视图,就是只查看数据,而不查看实现的视图:

在调试的时候,可以很方便的查看容器数据类型内的数据。

然后再仔细看了下Debugger for Java 的Changelog,发现还有一个比较有用的更新:

Add the source hyperlinks for the stack traces in the Debug Console output.

比如异常打印的StackTrace,在0.17.0是这样的:


为何一次请求会有两次HttpServlet:service调用?

今天看了下阿里出的 Arthas使用文档中有问到:

为什么只访问了http://localhost:8080/a.txt,但Arthas的trace命令打印出了两个请求树?

然后我去自己试了下,发现还真的是这个情况,trace日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
`---ts=2019-02-24 18:05:20;thread_name=http-nio-8080-exec-1;id=15;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@6601a0f8
`---[22.125552ms] javax.servlet.http.HttpServlet:service()
`---[22.048005ms] javax.servlet.http.HttpServlet:service()
`---[21.977486ms] org.springframework.web.servlet.FrameworkServlet:service()
+---[0.015829ms] javax.servlet.http.HttpServletRequest:getMethod()
+---[0.023379ms] org.springframework.http.HttpMethod:resolve()
`---[21.847595ms] org.springframework.web.servlet.HttpServletBean:service()
`---……

`---ts=2019-02-24 18:05:20;thread_name=http-nio-8080-exec-1;id=15;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@6601a0f8
`---[108.691709ms] javax.servlet.http.HttpServlet:service()
`---[108.658563ms] javax.servlet.http.HttpServlet:service()
`---[108.612929ms] org.springframework.web.servlet.FrameworkServlet:service()
+---[0.024764ms] javax.servlet.http.HttpServletRequest:getMethod()
+---[0.004569ms] org.springframework.http.HttpMethod:resolve()
`---[108.540682ms] org.springframework.web.servlet.HttpServletBean:service()
`---……

然后,过去翻了一下代码才发现,是org.apache.catalina.core.StandardHostValve#invoke方法的逻辑:

1
2
3
4
5
6
7
8
9
10
//正常的处理请求(这是第一个HttpServlet:service调用)  
context.getPipeline().getFirst().invoke(request, response);
// 如果response是失败的,那么再处理ErrorPage的逻辑
if (response.isErrorReportRequired()) {
if (t != null) {
throwable(request, response, t);
} else {
status(request, response);
}
}

在status方法中,获取ErrorPage,然后给request设置javax.servlet.error.*的属性,然后再forward到HttpServlet:service。这儿就会出现第二个HttpServlet:service请求。

那么问题来了,这个行为是规范里面有约定吗?

翻了翻 Java Servlet Specification,第10.9.2小节有说:

To allow developers to customize the appearance of content returned to a Web client when a servlet generates an error, the deployment descriptor defines a list of error page descriptions. The syntax allows the configuration of resources to be returned by the container either when a servlet or filter calls sendError on the response for specific status codes, or if the servlet generates an exception or error that propagates to the container.

If the sendError method is called on the response, the container consults the list of error page declarations for the Web application that use the status-code syntax and attempts a match. If there is a match, the container returns the resource as indicated by the location entry.

The Web application may have declared error pages using the exception-typeelement. In this case the container matches the exception type by comparing the exception thrown with the list of error-page definitions that use the exception-typeelement. A match results in the container returning the resource indicated in the location entry. The closest match in the class hierarchy wins.


Robert Lu

关注我的公众号