Java开发从工作到原理--BasicErrorController统一异常处理

依照Java开发从工作到原理--Spring Boot单体项目运行日志解析中提到的常见配置项修改后的配置信息如图:

IDEA社区版对properties文件不支持配置项提示功能,因此这里换成是yml文件

重新启动日志信息变为:

各项配置修改都已经生效

之前在DemoApplication类中,我们通过增加了一个方法实现了一个可访问的http API接口

代码未做任何修改

访问需加上content path配置前缀

扫描二维码关注公众号,回复: 8835690 查看本文章

这里是在DemoApplication类里面写的接口,正常情况是创建一个controller包用于专门存放controller类。

最简单的controller

直接运行,然后进行接口访问为如下结果:

报错返回404

错误提示中”This application has no explicit mapping for /error, so you are seeing this as a fallback.“,意思是说程序没有配置直接的错误页面,所以返回了这样一个统一的错误页面。

所以我们可以定义一个统一的错误页面。

SpringMVC是通过BasicErrorController进行统一的异常处理的,在IDEA中点两次SHIFT键可以唤起搜索框,输入BasicErrorController可以找到对应的.class文件,如果maven下载了源代码还可看到BasicErrorController.java源文件如下图:

唤起的搜索框中选择files tab

选择java文件可以直接查看源代码,选择class文件会看到idea反编译出来的代码,会提示是否下载源代码。

以看到@RequestMapping中的配置是支持EL表达式读取配置信息的

最主要的两个方法:

第一个方法向客户端返回html格式的错误信息,第二个方法向客户端返回json格式错误信息

给这两个方法都加上断点,以debug模式启动程序进行调试,查看SpringMVC的处理机制。

在浏览器访问http://localhost:8080/api/test/test地址后,

进入到errorHtml方法中

线程堆栈情况为:

DispatcherServlet之前的调用链为tomcat的调用链

到DispatcherServlet.onDispatch方法中查看的信息是

根据请求信息从handlerMappings中获取到的HandlerMethod就是BasicErrorController的errorHtml方法

进入getHandler方法打断点重新进行调试

设置条件断点,根据请求地址判断

此时handlerMappings中有7个对象

当执行到RequestMappingHandlerMapping获得了HandlerMethod为TestController.test方法

此次getHandler方法调用返回与最终的handlerMethod并不相同

而TestController.test的返回值为”thank you!“,SpringMvc将其作为要访问view的一个相对路径进行解析,与当前请求地址同一级别,获得view的真实访问路径为”/test/thank you!“,加上contextPath后完整路径为”/api/test/thank you!“,然后进行forward,因此DispatcherServlet接收到第二个请求如图:

如果TestController.test的返回值改为”/thank you!“,则SpringMVC解析的转发地址为"/thank you!",加上contextPath后完整路径为”/api/thank you!“,然后进行forward,因此DispatcherServlet接收到第二个请求如图:

而很显然这两个地址都没有对应controller和处理方法的,所以获得的mappedHandler是用来处理静态资源的ResourceHttpRequestHandler,很显然静态资源中依然没有对应的页面,所以会返回404状态码。而tomcat会根据状态码显示一个特定的页面,没有SpringBoot之前特定页面的配置是在web.xml中配置的

<error-page>
	<error-code>404</error-code>
	<location>/error/notFoundError.jsp</location>
</error-page>

这就涉及到Spring Boot中对于j2ee底层的一些操作,比如在SpringBoot中进行自定义Servlet,Filter,Listener,而这里配置的是ErrorPage。

还是在IDEA中点两次SHIFT键可以唤起搜索框,选择Files tab,输入ErrorPage,很幸运有很多结果:

选择在org.springframework包下的ErrorPage类

只是一个简单的类,没有任何注解

对三个构造函数进行Callers查询,得出的结果只有ErrorMvcAutoConfiguration类中的ErrorPageCustomizer内部静态类调用过。

方法中进行ErrorPage对象的创建并添加到ErrorPageRegistry对象中

对registerErrorPages方法进行Callers查询,得出结果只有ErrorPageRegistrarBeanPostProcessor.postProcessBeforeInitialization(ErrorPageRegistry registry)方法中调用过。这样SpringBoot默认有的ErrorPage的来处就找出来了:ErrorPageRegistrarBeanPostProcessor找出所有托管给Spring的ErrorPageRegistrar,由ErrorPageRegistrar将ErrorPage注册到ErrorPageRegistry中。

ErrorPageRegistry是一个接口,查看ErrorPageRegistry的实现类有

要跟http容器对接,所以tomcat,jetty,undertow对应的都有

目前项目使用tomcat,且并非reactive,所以使用的应该是TomcatServletWebServerFactory,而TomcatServletWebServerFactory.configureContext方法会将所有ErrorPage(springboot中的类)转换成tomcat中的ErrorPage对象,并添加到TomcatEmbeddedContext中。TomcatServletWebServerFactory.configureContext会在ApplicationContext.refresh方法中启动Web容器时调用。

回到ErrorPageCustomizer中ErrorPage的构造参数path的值为server.error.path的配置值,而这个值又是BasicErrorController类上配置的访问地址,因此当出现404异常时,由tomcat的error-page机制访问到BasicErrorController中的方法。

发布了6 篇原创文章 · 获赞 0 · 访问量 340

猜你喜欢

转载自blog.csdn.net/u013885298/article/details/104080385
今日推荐