千家信息网

使用@RestControllerAdvice怎么对异常进行处理

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,本篇文章给大家分享的是有关使用@RestControllerAdvice怎么对异常进行处理,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
千家信息网最后更新 2025年02月01日使用@RestControllerAdvice怎么对异常进行处理

本篇文章给大家分享的是有关使用@RestControllerAdvice怎么对异常进行处理,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

从DispatcherServlet入口,如下:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {    if (logger.isDebugEnabled()) {        String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";        logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +                " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");    }    // Keep a snapshot of the request attributes in case of an include,    // to be able to restore the original attributes after the include.    Map attributesSnapshot = null;    if (WebUtils.isIncludeRequest(request)) {        attributesSnapshot = new HashMap();        Enumeration attrNames = request.getAttributeNames();        while (attrNames.hasMoreElements()) {            String attrName = (String) attrNames.nextElement();            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {                attributesSnapshot.put(attrName, request.getAttribute(attrName));            }        }    }    // Make framework objects available to handlers and view objects.    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);    if (inputFlashMap != null) {        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));    }    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);    try {        //直接看这个方法        doDispatch(request, response);    }    finally {        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {            // Restore the original attribute snapshot, in case of an include.            if (attributesSnapshot != null) {                restoreAttributesAfterInclude(request, attributesSnapshot);            }        }    }}protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {        ModelAndView mv = null;        Exception dispatchException = null;        try {            processedRequest = checkMultipart(request);            multipartRequestParsed = (processedRequest != request);            // Determine handler for the current request.            mappedHandler = getHandler(processedRequest);            if (mappedHandler == null || mappedHandler.getHandler() == null) {                noHandlerFound(processedRequest, response);                return;            }            // Determine handler adapter for the current request.            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());            // Process last-modified header, if supported by the handler.            String method = request.getMethod();            boolean isGet = "GET".equals(method);            if (isGet || "HEAD".equals(method)) {                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());                if (logger.isDebugEnabled()) {                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);                }                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {                    return;                }            }            if (!mappedHandler.applyPreHandle(processedRequest, response)) {                return;            }            // Actually invoke the handler.            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());            if (asyncManager.isConcurrentHandlingStarted()) {                return;            }            applyDefaultViewName(processedRequest, mv);            mappedHandler.applyPostHandle(processedRequest, response, mv);        }        catch (Exception ex) {            dispatchException = ex;        }        catch (Throwable err) {            // As of 4.3, we're processing Errors thrown from handler methods as well,            // making them available for @ExceptionHandler methods and other scenarios.            dispatchException = new NestedServletException("Handler dispatch failed", err);        }        //处理结果(里面含异常处理)        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    }    catch (Exception ex) {        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);    }    catch (Throwable err) {        triggerAfterCompletion(processedRequest, response, mappedHandler,                new NestedServletException("Handler processing failed", err));    }    finally {        if (asyncManager.isConcurrentHandlingStarted()) {            // Instead of postHandle and afterCompletion            if (mappedHandler != null) {                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);            }        }        else {            // Clean up any resources used by a multipart request.            if (multipartRequestParsed) {                cleanupMultipart(processedRequest);            }        }    }}

进入异常处理的地方

进入到resolveException方法,默认解析器有3个,ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver;具体实例化的地方在WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers方法

protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers) {    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());    exceptionHandlerResolver.setMessageConverters(getMessageConverters());    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());    if (jackson2Present) {        exceptionHandlerResolver.setResponseBodyAdvice(                Collections.>singletonList(new JsonViewResponseBodyAdvice()));    }    exceptionHandlerResolver.setApplicationContext(this.applicationContext);    exceptionHandlerResolver.afterPropertiesSet();    exceptionResolvers.add(exceptionHandlerResolver);    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();    responseStatusResolver.setMessageSource(this.applicationContext);    exceptionResolvers.add(responseStatusResolver);    exceptionResolvers.add(new DefaultHandlerExceptionResolver());}

先看ExceptionHandlerExceptionResolver的处理如下: ExceptionHandlerExceptionResolver初始化的时候,

@Overridepublic void afterPropertiesSet() {    // Do this first, it may add ResponseBodyAdvice beans    initExceptionHandlerAdviceCache();    if (this.argumentResolvers == null) {        List resolvers = getDefaultArgumentResolvers();        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);    }    if (this.returnValueHandlers == null) {        List handlers = getDefaultReturnValueHandlers();        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);    }}

find所有ControllerAdvice Annotion注解的bean(找@ControllerAdvice注解或者其作为meta annotation,可以看下AnnotationUtils#findAnnotation方法),并排序;如果该类实现了ResponseBodyAdvice(可自定义扩展统一response的返回)接口

public ExceptionHandlerMethodResolver(Class handlerType) {    for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {        for (Class exceptionType : detectExceptionMappings(method)) {            addExceptionMapping(exceptionType, method);        }    }}public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {    @Override    public boolean matches(Method method) {        return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);    }};

找到标有ExceptionHandler注解的方法,并缓存再exceptionHandlerCache上,然后判断该ControllerAdviceBean是否能应用到该controller上

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {    Class handlerType = null;    if (handlerMethod != null) {        // Local exception handler methods on the controller class itself.        // To be invoked through the proxy, even in case of an interface-based proxy.        handlerType = handlerMethod.getBeanType();        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);        if (resolver == null) {            resolver = new ExceptionHandlerMethodResolver(handlerType);            this.exceptionHandlerCache.put(handlerType, resolver);        }        Method method = resolver.resolveMethod(exception);        if (method != null) {            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);        }        // For advice applicability check below (involving base packages, assignable types        // and annotation presence), use target class instead of interface-based proxy.        if (Proxy.isProxyClass(handlerType)) {            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());        }    }    for (Map.Entry entry : this.exceptionHandlerAdviceCache.entrySet()) {        ControllerAdviceBean advice = entry.getKey();        //判断是否匹配        if (advice.isApplicableToBeanType(handlerType)) {            ExceptionHandlerMethodResolver resolver = entry.getValue();            Method method = resolver.resolveMethod(exception);            if (method != null) {                return new ServletInvocableHandlerMethod(advice.resolveBean(), method);            }        }    }    return null;}

可以看一下ControllerAdvice注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface ControllerAdvice {/** * Alias for the {@link #basePackages} attribute. * 

Allows for more concise annotation declarations e.g.: * {@code @ControllerAdvice("org.my.pkg")} is equivalent to * {@code @ControllerAdvice(basePackages="org.my.pkg")}. * @since 4.0 * @see #basePackages() */@AliasFor("basePackages")String[] value() default {};/** * Array of base packages. *

Controllers that belong to those base packages or sub-packages thereof * will be included, e.g.: {@code @ControllerAdvice(basePackages="org.my.pkg")} * or {@code @ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"})}. *

{@link #value} is an alias for this attribute, simply allowing for * more concise use of the annotation. *

Also consider using {@link #basePackageClasses()} as a type-safe * alternative to String-based package names. * @since 4.0 */@AliasFor("value")String[] basePackages() default {};/** * Type-safe alternative to {@link #value()} for specifying the packages * to select Controllers to be assisted by the {@code @ControllerAdvice} * annotated class. *

Consider creating a special no-op marker class or interface in each package * that serves no purpose other than being referenced by this attribute. * @since 4.0 */Class[] basePackageClasses() default {};/** * Array of classes. *

Controllers that are assignable to at least one of the given types * will be assisted by the {@code @ControllerAdvice} annotated class. * @since 4.0 */Class[] assignableTypes() default {};/** * Array of annotations. *

Controllers that are annotated with this/one of those annotation(s) * will be assisted by the {@code @ControllerAdvice} annotated class. *

Consider creating a special annotation or use a predefined one, * like {@link RestController @RestController}. * @since 4.0 */Class[] annotations() default {};}

最后,定位到ServletInvocableHandlerMethod#invokeAndHandle异常方法处理 RequestResponseBodyMethodProcessor#handleReturnValue 通过消息解析器,把流写出去

protected  void writeWithMessageConverters(T value, MethodParameter returnType,        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {    Object outputValue;    Class valueType;    Type declaredType;    if (value instanceof CharSequence) {        outputValue = value.toString();        valueType = String.class;        declaredType = String.class;    }    else {        outputValue = value;        valueType = getReturnValueType(outputValue, returnType);        declaredType = getGenericType(returnType);    }    HttpServletRequest request = inputMessage.getServletRequest();    List requestedMediaTypes = getAcceptableMediaTypes(request);    List producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);    if (outputValue != null && producibleMediaTypes.isEmpty()) {        throw new IllegalArgumentException("No converter found for return value of type: " + valueType);    }    Set compatibleMediaTypes = new LinkedHashSet();    for (MediaType requestedType : requestedMediaTypes) {        for (MediaType producibleType : producibleMediaTypes) {            if (requestedType.isCompatibleWith(producibleType)) {                compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));            }        }    }    if (compatibleMediaTypes.isEmpty()) {        if (outputValue != null) {            throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);        }        return;    }    List mediaTypes = new ArrayList(compatibleMediaTypes);    MediaType.sortBySpecificityAndQuality(mediaTypes);    MediaType selectedMediaType = null;    for (MediaType mediaType : mediaTypes) {        if (mediaType.isConcrete()) {            selectedMediaType = mediaType;            break;        }        else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {            selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;            break;        }    }    if (selectedMediaType != null) {        selectedMediaType = selectedMediaType.removeQualityValue();        for (HttpMessageConverter messageConverter : this.messageConverters) {            if (messageConverter instanceof GenericHttpMessageConverter) {                if (((GenericHttpMessageConverter) messageConverter).canWrite(                        declaredType, valueType, selectedMediaType)) {                    outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,                            (Class>) messageConverter.getClass(),                            inputMessage, outputMessage);                    if (outputValue != null) {                        addContentDispositionHeader(inputMessage, outputMessage);                        ((GenericHttpMessageConverter) messageConverter).write(                                outputValue, declaredType, selectedMediaType, outputMessage);                        if (logger.isDebugEnabled()) {                            logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +                                    "\" using [" + messageConverter + "]");                        }                    }                    return;                }            }            else if (messageConverter.canWrite(valueType, selectedMediaType)) {                outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,                        (Class>) messageConverter.getClass(),                        inputMessage, outputMessage);                if (outputValue != null) {                    addContentDispositionHeader(inputMessage, outputMessage);                    ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);                    if (logger.isDebugEnabled()) {                        logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +                                "\" using [" + messageConverter + "]");                    }                }                return;            }        }    }    if (outputValue != null) {        throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);    }}

在body writer之前,会先调用ResponseBodyAdvice match做一层处理,也就是说,

@RestControllerAdvicepublic class GlobalExceptionHandler extends ResponseEntityExceptionHandler implements ResponseBodyAdvice {}

以上就是使用@RestControllerAdvice怎么对异常进行处理,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。

0