springweb篇6讲16常见错误今天我们来学习Spring异常处理机制提供了一套_第1页
springweb篇6讲16常见错误今天我们来学习Spring异常处理机制提供了一套_第2页
springweb篇6讲16常见错误今天我们来学习Spring异常处理机制提供了一套_第3页
springweb篇6讲16常见错误今天我们来学习Spring异常处理机制提供了一套_第4页
springweb篇6讲16常见错误今天我们来学习Spring异常处理机制提供了一套_第5页
已阅读5页,还剩11页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

publicvoiddestroy()}NotAllowException就是一个简单的RuntimeExceptionpublicpublicclassNotAllowExceptionextendsRuntimeExceptionpublicNotAllowException() 5同时,新增了一个RestControllerAdvice来处理这个异常,处理方式也很简单,就是返回一个403的resultCode:publicclassNotAllowExceptionHandlerpublicStringhandle()return"{\"resultCode\": 9为了验证一下失败的情况,我们模拟了一个请求,在HTTP请求头里加上一个Token,值为111,这样就会错误了,我们会不会被NotAllowExceptionHandler处理11throw想下问题出在哪呢?我们不妨对Spring我们先来回顾一下 从这张图中可以看出,当所有的过滤器被执行完毕以后,Spring才会进入Servlet相关的处理,而DispatcherServlet才是整个Servlet处理的,它是前端控制器设计模式的实现,提供SpringWebMVC的集中点并负责职责的分派。正是在这里,Spring处理其实说到这里,我们已经了解到过滤器内异常无法被统一处理的大致原因,就是因为异常处理发生在上图的红域,即DispatcherSerlet中的doDisach),而此时,过滤器已经全部执行完毕了。下面深入分析SpringWeb对异常统一处理的逻辑,深刻理解其内部原理首先我们来了解下ControllerAdvice是如何被Spring加载并对外的。在SpringWeb的配置类WebMvcConfigurationSupport中,被@Bean修饰的handlerExceptionResolveraddDefaultHandlerExceptionResolvers1123456789publicHandlerExceptionResolverhandlerExceptionResolver(@Qualifier("mvcContentNegotiationManager")ContentNegotiationManagerconList<HandlerExceptionResolver>exceptionResolvers=newArrayList<>();if(exceptionResolvers.isEmpty())}positecomposite=newreturn}最终按照下图的调用栈,Spring实例化了ExceptionHandlerExceptionResolver类。publicpublicvoidafterPropertiesSet()//Dothisfirst,itmayaddResponseBodyAdvice5并在initExceptionHandlerAdviceCache()中完成了所有ControllerAdvice中的ExceptionHandler@ControllerAdviceBean,把它们放到成员变量exceptionHandlerAdviceCache中。在我们这个案例里,就是指NotAllowExceptionHandler这个异常处理器。1123456789privatevoidinitExceptionHandlerAdviceCache()List<ControllerAdviceBean>adviceBeans=ControllerAdviceBean.findAnnotatedfor(ControllerAdviceBeanadviceBean:adviceBeans){Class<?>beanType=adviceBean.getBeanType();if(beanType==null){thrownewIllegalStateException("Unresolvabletypefor}ExceptionHandlerMethodResolverresolver=newifs())this.exceptionHandlerAdviceCache.put(adviceBean,}}到这,我们可以总结一下,WebMvcConfigurationSupport中的handlerExceptionResolver()实例化并了一个ExceptionHandlerExceptionResolver的实例,而所有被@ControllerAdvice注解修饰的异常处理器,都会在ExceptionHandlerExceptionResolver实例化的时候自动扫描并装载在其类成员变量exceptionHandlerAdviceCache中。privatevoidinitHandlerExceptionResolvers(ApplicationContextcontext)this.handlerExceptionResolversprivatevoidinitHandlerExceptionResolvers(ApplicationContextcontext)this.handlerExceptionResolvers=34(this.detectAllHandlerExceptionResolvers)5//FindallHandlerExceptionResolversintheApplicationContext,6Map<String,HandlerExceptionResolver>matchingBeans=7.beansOfTypeIncludingAncestors(context,8if(!matchingBeans.isEmpty())9this.handlerExceptionResolvers=new//WekeepHandlerExceptionResolversinsorted }14ControllerAdviceSpring下文贴出的是类DispatcherServlet中的方法doDispatch()的部分代码:1protectedvoiddoDispatch(HttpServletRequestrequest,234try5ModelAndViewmv=6ExceptiondispatchException=7try89//查找当前请求对应的handler}catch(Exceptionex)dispatchException=}catch(Throwableerr)dispatchException=newNestedServletException("Handler}processDispatchResult(processedRequest,processDispatchResult(processedRequest,response,mappedHandler,mv,}dispatchException,再交给processDispatchResultprivateprivatevoidprocessDispatchResult(HttpServletRequestrequest,@NullableHandlerExecutionChainmappedHandler,@NullableModelAndView@NullableExceptionexception)throwsExceptionbooleanerrorView=if(exception!=null)if(exceptioninstanceofModelAndViewDefiningException)mv=((ModelAndViewDefiningException) elseObjecthandler=(mappedHandler!=null?mv=processHandlerException(request,response,handler,errorView=(mv!= }ExceptionnullprocessHandlerExceptionprotectedprotectedModelAndViewprocessHandlerException(HttpServletRequestrequest,@NullableObjecthandler,Exceptionex)throwsExceptionModelAndViewexMv=if(this.handlerExceptionResolvers!=null)for(HandlerExceptionResolverresolver:exMv=resolver.resolveException(request,response,handler,if(exMv!=null) 14然后,processHandlerExceptionhandlerExceptionResolvers显然,这里的handlerExceptionResolvers一定包含我们的NotAllowExceptionHandler#NotAllowExceptionExceptionHandlerExceptionResolver包装类。SpringMVCFilter并将异常HandlerExceptionResolver进行解析处理。我们可以这样修改PermissionFilter,注入privateHandlerExceptionResolver12345612345678publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilHttpServletRequesthttpServletRequest=(HttpServletRequest)request;HttpServletResponsehttpServletResponse=(HttpServletResponse)responStringtoken=(!"111111".equals(token)){System.out.println("throwNotAllowException");resolver.resolveException(httpServletRequest,9}chain.doFilter(request,}当我们尝试用错误的Tokenthrowthrow返回的JSON11{"resultCode":成正确的Token案例2404继续沿用学生的案例,为了防止一些异常的,我们需要记录所有404状态的一般使用RESTfulJSON11{"resultCode":Spring404ExceptionHandlerpublicclassMyExceptionHandlerpublicStringhandle404()return"{\"resultCode\": 10URLregStudent成/regStudent1,得到了以下结果:11{"timestamp":"2021-05- ","status":404,"error":"NotSpring导致Spring没有使用我们定义的异常处理器呢?我们可以从异常处理的处理代码开始分析,DispatcherServlet中的doDispatch()核protectedprotectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponsemappedHandler=if(mappedHandler==null)noHandlerFound(processedRequest, 91123456789protectedvoidnoHandlerFound(HttpServletRequestrequest,(this.throwExceptionIfNoHandlerFound)thrownewNoHandlerFoundException(request.getMethod(),getRequestUri(reqnewServletServerHttpRequest(request).getHeaders());}else}}noHandlerFound()的逻辑非常简单,如果throwExceptionIfNoHandlerFound属性为true,则直接抛出NoHandlerFoundException异常,反之则会进一步获取到对应的请求到这,离我们非常近了,我们只需要将throwExceptionIfNoHandlerFound默认设trueNoHandlerFoundExceptiondoDispatch()内的catch俘获。进而就像案例1介绍的一样,最终能够执行我们自定义的异常处理器于是,我们开始尝试,因为throwExceptionIfNoHandlerFound对应的Spring配置项为throw-exception-if-no-handler-found,其加入到perties配置文件中,设置其值为true。实际上这里还存在另一个坑,在SpringWeb的WebMvcAutoConfiguration类中,其默认添加的两个ResourceHandler,一个是用来处理请求路径/webjars/**,而另一个是即前请求没有定义任何对应的请求处理器,getHandler()也一定会获取到一个Handler来处理当前请求,因为第二个匹配/**路径的ResourceHandler决定了任何请求路径都会被其处理。mappedHandler==null判断条件不会成立,显然就不可能走noHandlerFound(),那么就不会抛出NoHandlerFoundException异常,也无法被后续的ResourceHandler的详细逻辑。首先我们来了解下ControllerAdvice是如何被Spring加载并对外的同样是在WebMvcConfigurationSupport类中,被@Bean修饰的resourceHandlerMap(),它新建了ResourceHandlerRegistry类实例,并通过addResourceHandlers()将ResourceHandler到ResourceHandlerRegistry类实1123456789public(@Qualifier("mvcUrlPathHelper")UrlPathHelperurlPathHelper,@Qualifier("mvcPathMatcher")PathMatcherpathMatcher,@Qualifier("mvcContentNegotiationManager")ContentNegotiationManagercon@Qualifier("mvcConversionService")FormattingConversionServiceconversio@Qualifier("mvcResourceUrlProvider")ResourceUrlProviderresourceUrlProvAssert.state(this.applicationContext!=null,"NoApplicationContextset");Assert.state(this.servletContext!=null,"NoServletContextset");ResourceHandlerRegistryregistry=newResourceHandlerRegistry(this.applicathis.servletContext,contentNegotiationManager,urlPathHelper);=if(handlerMapreturnnull;}==null)return;}最终通过ResourceHandlerRegistry类实例中的getHandlerMap()返回了SimpleUrlHandlerMap实例,它装载了所有ResourceHandler的集合并到Spring容器中:1123456789()Map<String,HttpRequestHandler>urlMap=newfor(ResourceHandlerRegistrationregistration:this.registrations){for(StringpathPattern:registration.getPathPatterns()){ResourceHttpRequestHandlerhandler=urlMap.put(pathPattern,handler);}}returnnew(urlMap,}可以了解到,当前方法中的addResourceHandlers()最终执行到了WebMvcAutoConfigurationaddResourceHandlers(),通过这个方法,我们可以知道当前有哪些ResourceHandler的集合被到了Spring容器中:11publicvoidaddResourceHandlers(ResourceHandlerRegistryregistry)223456789ifs())logger.debug("Defaultresourcehandling}DurationcachePeriod=this.resourceProperties.getCache().getPeriod();CacheControlcacheControl=this.resourceProperties.getCache().getCachecontForPattern("/webjars/**"))}StringstaticPathPattern=ForPattern(staticPathPattern))}}ResourceHandler,一个是用来处理请求路径/webjars/**,而另一个是/**。this.resourceProperties.isAddMaps()为false,那么会直接返回,后续的两ResourceHandler也不会被添加。11234ifs())logger.debug("Defaultresourcehandling}至此,有两个ResourceHandler被实例化且到了Spirng容器中,一个处理路径/webjars/**的请求,另一个处理路径为/**同样,当第一次请求发生时,DispatcherServlet中的initHandlerMaps()将会获取所有到Spring的HandlerMap类型的实例,而SimpleUrlHandlerMap恰好实现了HandlerMap接口,这些SimpleUrlHandlerMap类型的实例则会被入到类成员变量handlerMaps中。7BeanFactoryUtils.beansOfTypeIncludingAncestors(context,8(!matchingBeans.isEmpty())9this.handlerMaps=new//WekeepHandlerMapsinsorted }}}123456privatevoids(ApplicationContextcontext)123456privatevoids(ApplicationContextcontext)s=//FindallMap<String,s)sintheApplicationContext,including>matchingBeans我们来回顾一下DispatcherServlet中的doDispatch()代码protectedprotectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponsemappedHandler=if(mappedHandler==null)noHandlerFound(processedRequest, 9这里的getHandler()将会遍历成员变量handlerMap112345678protectedHandlerExecutionChaingetHandler(HttpServletReq

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论