最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot接口如何統(tǒng)一異常處理

 更新時(shí)間:2022年07月12日 09:30:43   作者:Java?全棧知識(shí)體系  
這篇文章主要介紹了SpringBoot接口如何統(tǒng)一異常處理,SpringBoot接口如何對(duì)異常進(jìn)行統(tǒng)一封裝,并統(tǒng)一返回呢?以下文的參數(shù)校驗(yàn)為例,如何優(yōu)雅的將參數(shù)校驗(yàn)的錯(cuò)誤信息統(tǒng)一處理并封裝返回呢,感興趣的下下伙伴可以一同參考一下

為什么要優(yōu)雅的處理異常

如果我們不統(tǒng)一的處理異常,經(jīng)常會(huì)在controller層有大量的異常處理的代碼, 比如:

@Slf4j
@Api(value = "User Interfaces", tags = "User Interfaces")
@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * http://localhost:8080/user/add .
     *
     * @param userParam user param
     * @return user
     */
    @ApiOperation("Add User")
    @ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
    @PostMapping("add")
    public ResponseEntity<String> add(@Valid @RequestBody UserParam userParam) {
        // 每個(gè)接口充斥著大量的異常處理
        try {
            // do something
        } catch(Exception e) {
            return ResponseEntity.fail("error");
        }
        return ResponseEntity.ok("success");
    }
}

那怎么實(shí)現(xiàn)統(tǒng)一的異常處理,特別是結(jié)合參數(shù)校驗(yàn)等封裝?

實(shí)現(xiàn)案例

簡(jiǎn)單展示通過(guò)@ControllerAdvice進(jìn)行統(tǒng)一異常處理。

@ControllerAdvice異常統(tǒng)一處理

對(duì)于400參數(shù)錯(cuò)誤異常

/**
 * Global exception handler.
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * exception handler for bad request.
     *
     * @param e
     *            exception
     * @return ResponseResult
     */
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = { BindException.class, ValidationException.class, MethodArgumentNotValidException.class })
    public ResponseResult<ExceptionData> handleParameterVerificationException(@NonNull Exception e) {
        ExceptionData.ExceptionDataBuilder exceptionDataBuilder = ExceptionData.builder();
        log.warn("Exception: {}", e.getMessage());
        if (e instanceof BindException) {
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .forEach(exceptionDataBuilder::error);
        } else if (e instanceof ConstraintViolationException) {
            if (e.getMessage() != null) {
                exceptionDataBuilder.error(e.getMessage());
            }
        } else {
            exceptionDataBuilder.error("invalid parameter");
        }
        return ResponseResultEntity.fail(exceptionDataBuilder.build(), "invalid parameter");
    }

}

對(duì)于自定義異常

/**
 * handle business exception.
 *
 * @param businessException
 *            business exception
 * @return ResponseResult
 */
@ResponseBody
@ExceptionHandler(BusinessException.class)
public ResponseResult<BusinessException> processBusinessException(BusinessException businessException) {
    log.error(businessException.getLocalizedMessage(), businessException);
    // 這里可以屏蔽掉后臺(tái)的異常棧信息,直接返回"business error"
    return ResponseResultEntity.fail(businessException, businessException.getLocalizedMessage());
}

對(duì)于其它異常

/**
 * handle other exception.
 *
 * @param exception
 *            exception
 * @return ResponseResult
 */
@ResponseBody
@ExceptionHandler(Exception.class)
public ResponseResult<Exception> processException(Exception exception) {
    log.error(exception.getLocalizedMessage(), exception);
    // 這里可以屏蔽掉后臺(tái)的異常棧信息,直接返回"server error"
    return ResponseResultEntity.fail(exception, exception.getLocalizedMessage());
}

Controller接口

(接口中無(wú)需處理異常)

@Slf4j
@Api(value = "User Interfaces", tags = "User Interfaces")
@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * http://localhost:8080/user/add .
     *
     * @param userParam user param
     * @return user
     */
    @ApiOperation("Add User")
    @ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
    @PostMapping("add")
    public ResponseEntity<UserParam> add(@Valid @RequestBody UserParam userParam) {
        return ResponseEntity.ok(userParam);
    }
}

運(yùn)行測(cè)試

這里用postman測(cè)試下:

進(jìn)一步理解

我們?cè)偻ㄟ^(guò)一些問(wèn)題來(lái)幫助你更深入理解

@ControllerAdvice還可以怎么用?

除了通過(guò)@ExceptionHandler注解用于全局異常的處理之外,@ControllerAdvice還有兩個(gè)用法:

  • @InitBinder注解

用于請(qǐng)求中注冊(cè)自定義參數(shù)的解析,從而達(dá)到自定義請(qǐng)求參數(shù)格式的目的;

比如,在@ControllerAdvice注解的類中添加如下方法,來(lái)統(tǒng)一處理日期格式的格式化

@InitBinder
public void handleInitBinder(WebDataBinder dataBinder){
    dataBinder.registerCustomEditor(Date.class,
            new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}

Controller中傳入?yún)?shù)(string類型)自動(dòng)轉(zhuǎn)化為Date類型

@GetMapping("testDate")
public Date processApi(Date date) {
    return date;
}
  • @ModelAttribute注解

用來(lái)預(yù)設(shè)全局參數(shù),比如最典型的使用Spring Security時(shí)將添加當(dāng)前登錄的用戶信息(UserDetails)作為參數(shù)。

@ModelAttribute("currentUser")
public UserDetails modelAttribute() {
    return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

所有controller類中requestMapping方法都可以直接獲取并使用currentUser

@PostMapping("saveSomething")
public ResponseEntity<String> saveSomeObj(@ModelAttribute("currentUser") UserDetails operator) {
    // 保存操作,并設(shè)置當(dāng)前操作人員的ID(從UserDetails中獲得)
    return ResponseEntity.success("ok");
}

@ControllerAdvice是如何起作用的(原理)?

DispatcherServlet中onRefresh方法是初始化ApplicationContext后的回調(diào)方法,它會(huì)調(diào)用initStrategies方法,主要更新一些servlet需要使用的對(duì)象,包括國(guó)際化處理,requestMapping,視圖解析等等。

/**
    * This implementation calls {@link #initStrategies}.
    */
@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

/**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
    */
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); // 文件上傳
    initLocaleResolver(context); // i18n國(guó)際化
    initThemeResolver(context); // 主題
    initHandlerMappings(context); // requestMapping
    initHandlerAdapters(context); // adapters
    initHandlerExceptionResolvers(context); // 異常處理
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

從上述代碼看,如果要提供@ControllerAdvice提供的三種注解功能,從設(shè)計(jì)和實(shí)現(xiàn)的角度肯定是實(shí)現(xiàn)的代碼需要放在initStrategies方法中。

  • @ModelAttribute和@InitBinder處理

具體來(lái)看,如果你是設(shè)計(jì)者,很顯然容易想到:對(duì)于@ModelAttribute提供的參數(shù)預(yù)置和@InitBinder注解提供的預(yù)處理方法應(yīng)該是放在一個(gè)方法中的,因?yàn)樗鼈兌际窃谶M(jìn)入requestMapping方法前做的操作。

如下方法是獲取所有的HandlerAdapter,無(wú)非就是從BeanFactory中獲取

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }
    else {
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

我們要處理的是requestMapping的handlerResolver,作為設(shè)計(jì)者,就很容易出如下的結(jié)構(gòu)

在RequestMappingHandlerAdapter中的afterPropertiesSet去處理advice

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        // 緩存所有modelAttribute注解方法
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        // 緩存所有initBinder注解方法
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
}
  • @ExceptionHandler處理

@ExceptionHandler顯然是在上述initHandlerExceptionResolvers(context)方法中。

同樣的,從BeanFactory中獲取HandlerExceptionResolver

/**
    * Initialize the HandlerExceptionResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to no exception resolver.
    */
private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            HandlerExceptionResolver her =
                    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

我們很容易找到ExceptionHandlerExceptionResolver

同樣的在afterPropertiesSet去處理advice

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    initExceptionHandlerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}
private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        if (resolver.hasExceptionMappings()) {
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
        }
    }
}

到此這篇關(guān)于SpringBoot接口如何統(tǒng)一異常處理的文章就介紹到這了,更多相關(guān)SpringBoot接口 異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中Tomcat配置的示例代碼

    SpringBoot中Tomcat配置的示例代碼

    本文分享了在SpringBoot項(xiàng)目中配置Tomcat的一些心得和經(jīng)驗(yàn),包括Tomcat版本選擇、調(diào)整配置參數(shù)、自定義連接器、監(jiān)控和日志管理等方面,通過(guò)這些配置,可以有效提升應(yīng)用的性能、響應(yīng)速度和并發(fā)處理能力
    2024-11-11
  • 分布式鎖詳解以及Spring?Boot實(shí)戰(zhàn)示例代碼

    分布式鎖詳解以及Spring?Boot實(shí)戰(zhàn)示例代碼

    在分布式系統(tǒng)中,使用分布式鎖是保證共享資源獨(dú)占性的重要方式,這篇文章主要介紹了分布式鎖詳解及Spring?Boot實(shí)戰(zhàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • java字符串轉(zhuǎn)JSON簡(jiǎn)單代碼示例

    java字符串轉(zhuǎn)JSON簡(jiǎn)單代碼示例

    這篇文章主要給大家介紹了關(guān)于java字符串轉(zhuǎn)JSON的相關(guān)資料,JSON?是一種輕量級(jí)的數(shù)據(jù)交換格式,常用于Web應(yīng)用程序中的數(shù)據(jù)傳輸,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • Java解析xml的四種方法匯總

    Java解析xml的四種方法匯總

    XML在不同的語(yǔ)言里解析方式都是一樣的,只不過(guò)實(shí)現(xiàn)的語(yǔ)法不同而已。java中基本的解析方式有四種,DOM解析、sax解析、JDOM解析和DOM4J解析,下面我們就來(lái)詳細(xì)探討下這四種方式
    2016-05-05
  • Mybatis中自定義TypeHandler處理枚舉詳解

    Mybatis中自定義TypeHandler處理枚舉詳解

    本文主要介紹了Mybatis中自定義TypeHandler處理枚舉的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • Springboot創(chuàng)建時(shí)常用的依賴詳解

    Springboot創(chuàng)建時(shí)常用的依賴詳解

    本文介紹了Spring?Boot項(xiàng)目中常用依賴的配置及作用,涵蓋了父依賴、Web應(yīng)用、測(cè)試、數(shù)據(jù)庫(kù)、MyBatis、連接池、JSON處理、Lombok、AOP、校驗(yàn)、監(jiān)控、工具包、打包配置、多配置文件以及熱部署等
    2025-03-03
  • Java開(kāi)發(fā)崗位面試被問(wèn)到嵌套類怎么辦

    Java開(kāi)發(fā)崗位面試被問(wèn)到嵌套類怎么辦

    本篇文章主要介紹了深入理解Java嵌套類和內(nèi)部類,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-07-07
  • 使用BigDecimal除法后保留兩位小數(shù)

    使用BigDecimal除法后保留兩位小數(shù)

    這篇文章主要介紹了使用BigDecimal除法后保留兩位小數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例

    Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例

    這篇文章主要介紹了Spring MVC的文件上傳和下載以及攔截器的使用實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • 理解zookeeper選舉機(jī)制

    理解zookeeper選舉機(jī)制

    本文主要介紹了zookeeper選舉機(jī)制的相關(guān)知識(shí),具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02

最新評(píng)論

汝阳县| 呈贡县| 诸城市| 泰宁县| 阿坝县| 兴国县| 时尚| 蓝山县| 商水县| 金湖县| 吉木萨尔县| 太康县| 麻阳| 习水县| 宜兴市| 桂林市| 余干县| 兴义市| 阜新| 华池县| 宁波市| 南京市| 阳山县| 龙川县| 普安县| 镇远县| 来宾市| 祁东县| 洪江市| 上饶县| 诏安县| 东海县| 视频| 邮箱| 曲水县| 应城市| 广平县| 察隅县| 兴仁县| 高邑县| 宜昌市|