当前位置: 首页 > 科技观察

让错误处理再次交由Web服务器接管

时间:2023-03-20 10:35:46 科技观察

其实在web.根据错误码处理错误,如下图01:根据异常类型处理错误,如下图02:不过,我们比较熟悉的应该是SpringMVC的统一异常处理。如图03所示:@ControllerAdvice注解和@ExceptionHandler注解你应该很熟悉了。处理原理是当业务控制器抛出异常时,根据异常类型查找并执行相应的方法。可以看出,整个异常处理过程都是在SpringMVC内部完成的,根本没有涉及tomcat等web服务器。那么问题来了,明明web服务器可以处理异常,为什么SpringMVC还要自己处理呢?我不想,或者我有其他困难。看过的人应该能猜到一些。因为用SpringMVC开发的项目最后打包成war包,然后扔到tomcat下。而且SpringMVC和tomcat是通过JavaWeb规范连接的,没有办法自由交互。但是tomcat的错误处理需要在web.xml中进行配置。严格来说,web.xml和SpringMVC关系不大。特别是在Spring全面进入Java和注解配置时代之后,web.xml逐渐被弱化,然后变得可有可无,直到最后彻底消失。所以(我猜想)SpringMVC可能不希望它的用户在一个与自己关系不大的web.xml中配置一些业务相关的异常处理映射。所以只好自己消化异常处理了。不用担心tomcat。这就是SpringMVC统一处理异常的原因。让web服务器重新处理错误当历史来到了SpringBoot的时代,SpringBoot翻身成为了入口,而web服务器竟然是一个组件。SpringBoot可以操作web服务器的API,通过编程对web服务器进行深度配置。很多事情变得更容易,比如错误处理。因为web.xml中的错误处理映射最终是在tomcat中注册的,所以SpringBoot只需要操作tomcatAPI,使用编程注册一些错误处理映射即可。因为用户直接和SpringBoot打交道,所以SpringBoot需要抽象出一套错误处理注册机制供用户注册。这样,SpringBoot在获取到用户注册的错误处理映射信息后,在生成web服务器(如tomcat)时,就可以将映射信息添加到web服务器中。SpringBoot注册错误处理映射的方案我们来看看错误处理映射是如何描述的,如下图04所示:三个字段的含义是:path是一个路径,表示错误处理的url。status为状态码,如404、500等。exception为异常类型,如LoginFailedException。有3种使用方式:1)状态+路径,比如404+/404,意思是遇到404就执行url/404。2)exception+path,比如LoginFailedException+/loginfailed,表示遇到登录失败异常,执行下面的url3)没有status和exception,只有path,相当于通配符,匹配所有异常情况.接下来,是时候考虑如何注册了。照例提供接口即可,如下图0506:Spring是基于bean的,那么SpringBoot提供的方法当然是和bean相关的。只要在容器中注册一个这种接口类型的bean,如下图07所示:信息会被收集和存储,如图08所示:然后在创建web服务器的时候添加。以tomcat为例,如图09所示:可以看到最终转换为tomcat的error映射,如下图10所示:这里的三个setter方法setLocation、setErrorCode、setExceptionType分别对应三个标签,,,在web.xml中。此时,错误处理已经注册。还是需要SpringBoot协助处理错误。有一点要明白,SpringMVC交给tomcat的只是错误处理映射的匹配,而一些真正的错误处理还是需要自己去完成。所以整个过程是这样的,SpringMVC随意抛出一个异常,这个异常就会抛到tomcat中。Tomcat获取异常类型,根据注册的错误处理映射关系找到一个url,然后调用这个url。那么这个url指向哪里呢?大概率是回到了SpringMVC。是不是很有趣,哈哈。Tomcat就像一面镜子,SpringMVC向它发出一束光,反射回来。只是发出了一个异常并返回了一个url。这个url对应一个可以处理这个错误的Controller方法。这样执行这个Controller方法就相当于处理了异常。这个Controller是一个可以处理错误的Controller,所以叫做ErrorController。如下图11所示:其实主要是一个Marker接口,也起到标志的作用。下面是真正用来处理错误的Controller,它实现了刚才的logo接口,如下图12所示:@RequestMapping方法用来处理错误,可以返回JSON,也可以查看页面。我们可以自己写一个错误处理类,然后继承这个类,添加自己的错误处理方法,最后使用@Controller注解重新注册。您可以将异常映射到与其名称相同的url路径,例如将Exception映射到/Exception,如下图13所示:这两种方法中,一种返回html页面,另一种返回JSON。备注:异常类型与其映射的url的关系可以根据自己的需要规划,规则统一即可。处理错误时,自然要获取错误相关的信息。可以满足这个接口,如下图14所示:可以得到Error属性和抛出的异常对象。可以看出接口的两个方法都有参数WebRequest,表示错误信息是从请求中获取的。同时也说明有些错误信息是别人特意放在request中的,是SpringBoot放的,还是web服务器放的?事实上,它们都有。比如异常对象是SpringBoot放的,响应状态码是web服务器(根据Javaweb规范)放的。这也表明,Web服务器在执行错误处理的url时使用转发而不是重定向,因为应该保留请求中的信息。最后一个是错误视图解析器,如下图15所示:SpringBoot提供了一个默认的视图解析器实现。默认情况下,到classpath下的error目录下找到.html视图页面。下图16:支持状态码到页面的映射。比如404默认映射到/error/404.html,500默认映射到/error/500.html。如果没有这个特定的页面,也支持系列映射,例如404为/error/4xx.html,500为5xx.html。当然,默认的一般不能满足要求。我们可以继承这个默认类,然后重写视图解析方法。最后,只要将这个类注册为一个bean,这样SpringBoot就会使用我们的类。下图17:本文总结,重要1)用户使用SpringBoot提供的错误处理映射机制,注册状态码和异常映射url信息。2)这些映射信息最终会被注册到web服务器中,比如tomcat。3)SpringBoot向web服务器抛出异常,web服务器根据异常找到对应的url并执行。4)流程再次回到SpringBoot,进入错误处理Controller方法,执行错误处理方法。5)如果需要解析视图,使用error视图解析器进行视图解析。否则直接返回JSON。其中,用户可以参与的只有三个步骤,注册异常映射,扩展错误处理Controller,扩展错误视图解析器。文中有具体的参与方法,无非就是实现一个接口或者继承某个类,然后注册为bean。