SpringBoot接口如何統(tǒng)一異常處理_第1頁
SpringBoot接口如何統(tǒng)一異常處理_第2頁
SpringBoot接口如何統(tǒng)一異常處理_第3頁
SpringBoot接口如何統(tǒng)一異常處理_第4頁
SpringBoot接口如何統(tǒng)一異常處理_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第SpringBoot接口如何統(tǒng)一異常處理目錄為什么要優(yōu)雅的處理異常實現案例@ControllerAdvice異常統(tǒng)一處理Controller接口運行測試進一步理解@ControllerAdvice還可以怎么用?@ControllerAdvice是如何起作用的(原理)?

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

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

@Slf4j

@Api(value="UserInterfaces",tags="UserInterfaces")

@RestController

@RequestMapping("/user")

publicclassUserController{

*http://localhost:8080/user/add.

*@paramuserParamuserparam

*@returnuser

@ApiOperation("AddUser")

@ApiImplicitParam(name="userParam",type="body",dataTypeClass=UserParam.class,required=true)

@PostMapping("add")

publicResponseEntityStringadd(@Valid@RequestBodyUserParamuserParam){

//每個接口充斥著大量的異常處理

try{

//dosomething

}catch(Exceptione){

returnResponseEntity.fail("error");

returnResponseEntity.ok("success");

}

那怎么實現統(tǒng)一的異常處理,特別是結合參數校驗等封裝?

實現案例

簡單展示通過@ControllerAdvice進行統(tǒng)一異常處理。

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

對于400參數錯誤異常

/**

*Globalexceptionhandler.

@Slf4j

@RestControllerAdvice

publicclassGlobalExceptionHandler{

*exceptionhandlerforbadrequest.

*@parame

*exception

*@returnResponseResult

@ResponseBody

@ResponseStatus(code=HttpStatus.BAD_REQUEST)

@ExceptionHandler(value={BindException.class,ValidationException.class,MethodArgumentNotValidException.class})

publicResponseResultExceptionDatahandleParameterVerificationException(@NonNullExceptione){

ExceptionData.ExceptionDataBuilderexceptionDataBuilder=ExceptionData.builder();

log.warn("Exception:{}",e.getMessage());

if(einstanceofBindException){

BindingResultbindingResult=((MethodArgumentNotValidException)e).getBindingResult();

bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)

.forEach(exceptionDataBuilder::error);

}elseif(einstanceofConstraintViolationException){

if(e.getMessage()!=null){

exceptionDataBuilder.error(e.getMessage());

}else{

exceptionDataBuilder.error("invalidparameter");

returnResponseResultEntity.fail(exceptionDataBuilder.build(),"invalidparameter");

對于自定義異常

/**

*handlebusinessexception.

*@parambusinessException

*businessexception

*@returnResponseResult

@ResponseBody

@ExceptionHandler(BusinessException.class)

publicResponseResultBusinessExceptionprocessBusinessException(BusinessExceptionbusinessException){

log.error(businessException.getLocalizedMessage(),businessException);

//這里可以屏蔽掉后臺的異常棧信息,直接返回"businesserror"

returnResponseResultEntity.fail(businessException,businessException.getLocalizedMessage());

}

對于其它異常

/**

*handleotherexception.

*@paramexception

*exception

*@returnResponseResult

@ResponseBody

@ExceptionHandler(Exception.class)

publicResponseResultExceptionprocessException(Exceptionexception){

log.error(exception.getLocalizedMessage(),exception);

//這里可以屏蔽掉后臺的異常棧信息,直接返回"servererror"

returnResponseResultEntity.fail(exception,exception.getLocalizedMessage());

}

Controller接口

(接口中無需處理異常)

@Slf4j

@Api(value="UserInterfaces",tags="UserInterfaces")

@RestController

@RequestMapping("/user")

publicclassUserController{

*http://localhost:8080/user/add.

*@paramuserParamuserparam

*@returnuser

@ApiOperation("AddUser")

@ApiImplicitParam(name="userParam",type="body",dataTypeClass=UserParam.class,required=true)

@PostMapping("add")

publicResponseEntityUserParamadd(@Valid@RequestBodyUserParamuserParam){

returnResponseEntity.ok(userParam);

}

運行測試

這里用postman測試下:

進一步理解

我們再通過一些問題來幫助你更深入理解

@ControllerAdvice還可以怎么用?

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

@InitBinder注解

用于請求中注冊自定義參數的解析,從而達到自定義請求參數格式的目的;

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

@InitBinder

publicvoidhandleInitBinder(WebDataBinderdataBinder){

dataBinder.registerCustomEditor(Date.class,

newCustomDateEditor(newSimpleDateFormat("yyyy-MM-dd"),false));

}

Controller中傳入參數(string類型)自動轉化為Date類型

@GetMapping("testDate")

publicDateprocessApi(Datedate){

returndate;

}

@ModelAttribute注解

用來預設全局參數,比如最典型的使用SpringSecurity時將添加當前登錄的用戶信息(UserDetails)作為參數。

@ModelAttribute("currentUser")

publicUserDetailsmodelAttribute(){

return(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

}

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

@PostMapping("saveSomething")

publicResponseEntityStringsaveSomeObj(@ModelAttribute("currentUser")UserDetailsoperator){

//保存操作,并設置當前操作人員的ID(從UserDetails中獲得)

returnResponseEntity.success("ok");

}

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

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

/**

*Thisimplementationcalls{@link#initStrategies}.

@Override

protectedvoidonRefresh(ApplicationContextcontext){

initStrategies(context);

*Initializethestrategyobjectsthatthisservletuses.

*pMaybeoverriddeninsubclassesinordertoinitializefurtherstrategyobjects.

protectedvoidinitStrategies(ApplicationContextcontext){

initMultipartResolver(context);//文件上傳

initLocaleResolver(context);//i18n國際化

initThemeResolver(context);//主題

initHandlerMappings(context);//requestMapping

initHandlerAdapters(context);//adapters

initHandlerExceptionResolvers(context);//異常處理

initRequestToViewNameTranslator(context);

initViewResolvers(context);

initFlashMapManager(context);

}

從上述代碼看,如果要提供@ControllerAdvice提供的三種注解功能,從設計和實現的角度肯定是實現的代碼需要放在initStrategies方法中。

@ModelAttribute和@InitBinder處理

具體來看,如果你是設計者,很顯然容易想到:對于@ModelAttribute提供的參數預置和@InitBinder注解提供的預處理方法應該是放在一個方法中的,因為它們都是在進入requestMapping方法前做的操作。

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

privatevoidinitHandlerAdapters(ApplicationContextcontext){

this.handlerAdapters=null;

if(this.detectAllHandlerAdapters){

//FindallHandlerAdaptersintheApplicationContext,includingancestorcontexts.

MapString,HandlerAdaptermatchingBeans=

BeanFactoryUtils.beansOfTypeIncludingAncestors(context,HandlerAdapter.class,true,false);

if(!matchingBeans.isEmpty()){

this.handlerAdapters=newArrayList(matchingBeans.values());

//WekeepHandlerAdaptersinsortedorder.

AnnotationAwareOrderComparator.sort(this.handlerAdapters);

else{

try{

HandlerAdapterha=context.getBean(HANDLER_ADAPTER_BEAN_NAME,HandlerAdapter.class);

this.handlerAdapters=Collections.singletonList(ha);

catch(NoSuchBeanDefinitionExceptionex){

//Ignore,we'lladdadefaultHandlerAdapterlater.

//EnsurewehaveatleastsomeHandlerAdapters,byregistering

//defaultHandlerAdaptersifnootheradaptersarefound.

if(this.handlerAdapters==null){

this.handlerAdapters=getDefaultStrategies(context,HandlerAdapter.class);

if(logger.isTraceEnabled()){

logger.trace("NoHandlerAdaptersdeclaredforservlet'"+getServletName()+

"':usingdefaultstrategiesfromDispatcherSperties");

}

我們要處理的是requestMapping的handlerResolver,作為設計者,就很容易出如下的結構

在RequestMappingHandlerAdapter中的afterPropertiesSet去處理advice

@Override

publicvoidafterPropertiesSet(){

//Dothisfirst,itmayaddResponseBodyadvicebeans

initControllerAdviceCache();

if(this.argumentResolvers==null){

ListHandlerMethodArgumentResolverresolvers=getDefaultArgumentResolvers();

this.argumentResolvers=newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);

if(this.initBinderArgumentResolvers==null){

ListHandlerMethodArgumentResolverresolvers=getDefaultInitBinderArgumentResolvers();

this.initBinderArgumentResolvers=newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);

if(this.returnValueHandlers==null){

ListHandlerMethodReturnValueHandlerhandlers=getDefaultReturnValueHandlers();

this.returnValueHandlers=newHandlerMethodReturnValueHandlerComposite().addHandlers(handlers);

privatevoidinitControllerAdviceCache(){

if(getApplicationContext()==null){

return;

ListControllerAdviceBeanadviceBeans=ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

ListObjectrequestResponseBodyAdviceBeans=newArrayList();

for(ControllerAdviceBeanadviceBean:adviceBeans){

ClassbeanType=adviceBean.getBeanType();

if(beanType==null){

thrownewIllegalStateException("UnresolvabletypeforControllerAdviceBean:"+adviceBean);

//緩存所有modelAttribute注解方法

SetMethodattrMethods=MethodIntrospector.selectMethods(beanType,MODEL_ATTRIBUTE_METHODS);

if(!attrMethods.isEmpty()){

this.modelAttributeAdviceCache.put(adviceBean,attrMethods);

//緩存所有initBinder注解方法

SetMethodbinderMethods=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

/**

*InitializetheHandlerExceptionResolverusedbythisclass.

*pIfnobeanisdefinedwiththegivennameintheBeanFactoryforthisnamespace,

*wedefaulttonoexceptionresolver.

privatevoidinitHandlerExceptionResolvers(ApplicationContextcontext){

this.handlerExceptionResolvers=null;

if(this.detectAllHandlerExceptionResolvers){

//FindallHandlerExceptionResolversintheApplicationContext,includingancestorcontexts.

MapString,HandlerExceptionResolvermatchingBeans=BeanFactoryUtils

.beansOfTypeIncludingAncestors(context,HandlerExceptionResolver.class,true,false);

if(!matchingBeans.isEmpty()){

this.handlerExceptionResolvers=newArrayList(matchingBeans.values());

//WekeepHandlerExceptionResolversinsortedorder.

AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);

else{

try{

HandlerExceptionResolverher=

context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME,HandlerExceptionResolver.class);

this.handlerExceptionResolvers=Collections.singletonList(her);

catch(NoSuchBeanDefinitionExceptionex){

//Ignore,noHandlerExceptionResolverisfinetoo.

//EnsurewehaveatleastsomeHandlerExceptionResolvers,byregistering

//defaultHandlerExceptionResolversifnootherresolversarefound.

if(this.handlerExceptionResolvers==null){

this.handlerExceptionResolvers=getDefaultStrategies(context,HandlerExceptionResolver.class);

if(logger.isTraceEnabled()){

logger.trace("NoHandlerExceptionResolversdeclaredinservlet'"+getServletName()+

"':usingdefaultstrategiesfromDispatcherSperties");

}

我們很容易找到ExceptionHandlerExceptionResolver

同樣的在afterPropertiesSet去處理advice

@Override

publicvoidafterPropertiesSet(){

//Dothisfirst,itmayaddRes

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論