




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030中國甜玉米行業(yè)市場現狀供需分析及投資評估規(guī)劃分析研究報告
- 2025-2030中國海外醫(yī)療行業(yè)市場發(fā)展前瞻及投資戰(zhàn)略研究報告
- 2025-2030中國治理風險和合規(guī)平臺行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- DB32/T 4275-2022鋼結構用高強度大六角頭螺栓連接副扭矩系數的測定
- DB32/T 4240-2022白玉菇和蟹味菇工廠化生產技術規(guī)程
- DB32/T 4080.2-2021中藥智能制造技術規(guī)程第2部分:提取應用系統(tǒng)
- DB32/T 4018-2021“綠領青梗菜”小白菜生產技術規(guī)程
- DB32/T 3838-2020梨病蟲害綠色減量防控技術規(guī)程
- DB32/T 3779-2020茄果類蔬菜設施栽培水肥一體化技術規(guī)程
- DB32/T 3761.6-2020新型冠狀病毒肺炎疫情防控技術規(guī)范第6部分:公共場所
- 科技成果-電解鋁煙氣脫硫脫氟除塵一體化技術
- YS/T 273.12-2006冰晶石化學分析方法和物理性能測定方法 第12部分:火焰原子吸收光譜法測定氧化鈣含量
- GB/T 39171-2020廢塑料回收技術規(guī)范
- 2015山東高考英語試題及答案
- GB/T 18964.2-2003塑料抗沖擊聚苯乙烯(PS-I)模塑和擠出材料第2部分:試樣制備和性能測定
- GA/T 1661-2019法醫(yī)學關節(jié)活動度檢驗規(guī)范
- 他達拉非課件
- 資料交接移交確認單
- 風對起飛和著陸影響及修正和風切變完整版課件
- 大數據時代的互聯網信息安全題庫
- DL∕T 1776-2017 電力系統(tǒng)用交流濾波電容器技術導則
評論
0/150
提交評論