国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

掌握這幾個減少 try catch 的方法,讓代碼更簡潔!

共 21868字,需瀏覽 44分鐘

 ·

2022-05-31 17:46

來源:cnblogs.com/jurendage/p/11255197.html

背景

軟件開發(fā)過程中,不可避免的是需要處理各種異常,就我自己來說,至少有一半以上的時間都是在處理各種異常情況,所以代碼中就會出現(xiàn)大量的try {...} catch {...} finally {...} 代碼塊,不僅有大量的冗余代碼,而且還影響代碼的可讀性。比較下面兩張圖,看看您現(xiàn)在編寫的代碼屬于哪一種風(fēng)格?然后哪種編碼風(fēng)格您更喜歡?

丑陋的 try catch 代碼塊

優(yōu)雅的Controller

上面的示例,還只是在Controller層,如果是在Service層,可能會有更多的try catch代碼塊。這將會嚴(yán)重影響代碼的可讀性、“美觀性”。

所以如果是我的話,我肯定偏向于第二種,我可以把更多的精力放在業(yè)務(wù)代碼的開發(fā),同時代碼也會變得更加簡潔。

既然業(yè)務(wù)代碼不顯式地對異常進(jìn)行捕獲、處理,而異??隙ㄟ€是處理的,不然系統(tǒng)豈不是動不動就崩潰了,所以必須得有其他地方捕獲并處理這些異常。公眾號(Java后端)還發(fā)布過很多編程技巧文章,關(guān)注「Java后端」回復(fù) 666 下載。

那么問題來了,如何優(yōu)雅的處理各種異常?

什么是統(tǒng)一異常處理

Spring在3.2版本增加了一個注解@ControllerAdvice,可以與@ExceptionHandler@InitBinder、@ModelAttribute 等注解注解配套使用,對于這幾個注解的作用,這里不做過多贅述,若有不了解的,可以參考Spring3.2新注解@ControllerAdvice,先大概有個了解。

不過跟異常處理相關(guān)的只有注解@ExceptionHandler,從字面上看,就是 異常處理器 的意思,其實際作用也是:若在某個Controller類定義一個異常處理方法,并在方法上添加該注解,那么當(dāng)出現(xiàn)指定的異常時,會執(zhí)行該處理異常的方法,其可以使用springmvc提供的數(shù)據(jù)綁定,比如注入HttpServletRequest等,還可以接受一個當(dāng)前拋出的Throwable對象。

但是,這樣一來,就必須在每一個Controller類都定義一套這樣的異常處理方法,因為異??梢允歉鞣N各樣。這樣一來,就會造成大量的冗余代碼,而且若需要新增一種異常的處理邏輯,就必須修改所有Controller類了,很不優(yōu)雅。

當(dāng)然你可能會說,那就定義個類似BaseController的基類,這樣總行了吧。

這種做法雖然沒錯,但仍不盡善盡美,因為這樣的代碼有一定的侵入性和耦合性。簡簡單單的Controller,我為啥非得繼承這樣一個類呢,萬一已經(jīng)繼承其他基類了呢。大家都知道Java只能繼承一個類。

那有沒有一種方案,既不需要跟Controller耦合,也可以將定義的 異常處理器 應(yīng)用到所有控制器呢?所以注解@ControllerAdvice出現(xiàn)了,簡單的說,該注解可以把異常處理器應(yīng)用到所有控制器,而不是單個控制器。借助該注解,我們可以實現(xiàn):在獨立的某個地方,比如單獨一個類,定義一套對各種異常的處理機制,然后在類的簽名加上注解@ControllerAdvice,統(tǒng)一對 不同階段的、不同異常 進(jìn)行處理。這就是統(tǒng)一異常處理的原理。

注意到上面對異常按階段進(jìn)行分類,大體可以分成:進(jìn)入Controller前的異常 和 Service 層異常,具體可以參考下圖:

1321bb8c2952cbb47ed4687c598d2f7e.png

不同階段的異常

目標(biāo)

消滅95%以上的 try catch 代碼塊,以優(yōu)雅的 Assert(斷言) 方式來校驗業(yè)務(wù)的異常情況,只關(guān)注業(yè)務(wù)邏輯,而不用花費大量精力寫冗余的 try catch 代碼塊。

統(tǒng)一異常處理實戰(zhàn)

在定義統(tǒng)一異常處理類之前,先來介紹一下如何優(yōu)雅的判定異常情況并拋異常。

用 Assert(斷言) 替換 throw exception**

想必 Assert(斷言) 大家都很熟悉,比如 Spring 家族的 org.springframework.util.Assert,在我們寫測試用例的時候經(jīng)常會用到,使用斷言能讓我們編碼的時候有一種非一般絲滑的感覺,比如:

@Test
public?void?test1()?{
??...
??User?user?=?userDao.selectById(userId);
??Assert.notNull(user,?"用戶不存在.");
??...
}
?
@Test
public?void?test2()?{
??//?另一種寫法
??User?user?=?userDao.selectById(userId);
??if?(user?==?null)?{
????throw?new?IllegalArgumentException("用戶不存在.");
??}
}

有沒有感覺第一種判定非空的寫法很優(yōu)雅,第二種寫法則是相對丑陋的 if {...} 代碼塊。那么 神奇的 Assert.notNull() 背后到底做了什么呢?下面是 Assert 的部分源碼:

public?abstract?class?Assert?{
????public?Assert()?{
????}
?
????public?static?void?notNull(@Nullable?Object?object,?String?message)?{
????????if?(object?==?null)?{
????????????throw?new?IllegalArgumentException(message);
????????}
????}
}

可以看到,Assert 其實就是幫我們把 if {...} 封裝了一下,是不是很神奇。雖然很簡單,但不可否認(rèn)的是編碼體驗至少提升了一個檔次。那么我們能不能模仿org.springframework.util.Assert,也寫一個斷言類,不過斷言失敗后拋出的異常不是IllegalArgumentException 這些內(nèi)置異常,而是我們自己定義的異常。下面讓我們來嘗試一下。

public?interface?Assert?{
????/**
?????*?創(chuàng)建異常
?????*?@param?args
?????*?@return
?????*/

????BaseException?newException(Object...?args);
?
????/**
?????*?創(chuàng)建異常
?????*?@param?t
?????*?@param?args
?????*?@return
?????*/

????BaseException?newException(Throwable?t,?Object...?args);
?
????/**
?????*?

斷言對象obj非空。如果對象obj為空,則拋出異常
?????*
?????*?@param?obj?待判斷對象
?????*/
????default?void?assertNotNull(Object?obj)?{
????????if?(obj?==?null)?{
????????????throw?newException(obj);
????????}
????}
?
????/**
?????*?

斷言對象obj非空。如果對象obj為空,則拋出異常
?????*?

異常信息message支持傳遞參數(shù)方式,避免在判斷之前進(jìn)行字符串拼接操作
?????*
?????*?@param?obj?待判斷對象
?????*?@param?args?message占位符對應(yīng)的參數(shù)列表
?????*/
????default?void?assertNotNull(Object?obj,?Object...?args)?{
????????if?(obj?==?null)?{
????????????throw?newException(args);
????????}
????}
}

上面的Assert斷言方法是使用接口的默認(rèn)方法定義的,然后有沒有發(fā)現(xiàn)當(dāng)斷言失敗后,拋出的異常不是具體的某個異常,而是交由2個newException接口方法提供。因為業(yè)務(wù)邏輯中出現(xiàn)的異常基本都是對應(yīng)特定的場景,比如根據(jù)用戶id獲取用戶信息,查詢結(jié)果為null,此時拋出的異??赡転?code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;font-size: 12px;color: rgb(71, 193, 168);">UserNotFoundException,并且有特定的異常碼(比如7001)和異常信息“用戶不存在”。所以具體拋出什么異常,有Assert的實現(xiàn)類決定。(此處推薦一下前日的文章 去掉煩人的 !=null)

看到這里,您可能會有這樣的疑問,按照上面的說法,那豈不是有多少異常情況,就得有定義等量的斷言類和異常類,這顯然是反人類的,這也沒想象中高明嘛。別急,且聽我細(xì)細(xì)道來。

善解人意的Enum

自定義異常BaseException有2個屬性,即code、message,這樣一對屬性,有沒有想到什么類一般也會定義這2個屬性?沒錯,就是枚舉類。且看我如何將 EnumAssert 結(jié)合起來,相信我一定會讓你眼前一亮。如下:

public?interface?IResponseEnum?{
????int?getCode();
????String?getMessage();
}
/**
?*?

業(yè)務(wù)異常


?*?

業(yè)務(wù)處理時,出現(xiàn)異常,可以拋出該異常


?*/

public?class?BusinessException?extends??BaseException?{
?
????private?static?final?long?serialVersionUID?=?1L;
?
????public?BusinessException(IResponseEnum?responseEnum,?Object[]?args,?String?message)?{
????????super(responseEnum,?args,?message);
????}
?
????public?BusinessException(IResponseEnum?responseEnum,?Object[]?args,?String?message,?Throwable?cause)?{
????????super(responseEnum,?args,?message,?cause);
????}
}
public?interface?BusinessExceptionAssert?extends?IResponseEnum,?Assert?{
?
????@Override
????default?BaseException?newException(Object...?args)?{
????????String?msg?=?MessageFormat.format(this.getMessage(),?args);
?
????????return?new?BusinessException(this,?args,?msg);
????}
?
????@Override
????default?BaseException?newException(Throwable?t,?Object...?args)?{
????????String?msg?=?MessageFormat.format(this.getMessage(),?args);
?
????????return?new?BusinessException(this,?args,?msg,?t);
????}
?
}
@Getter
@AllArgsConstructor
public?enum?ResponseEnum?implements?BusinessExceptionAssert?{
?
????/**
?????*?Bad?licence?type
?????*/

????BAD_LICENCE_TYPE(7001,?"Bad?licence?type."),
????/**
?????*?Licence?not?found
?????*/

????LICENCE_NOT_FOUND(7002,?"Licence?not?found.")
????;
?
????/**
?????*?返回碼
?????*/

????private?int?code;
????/**
?????*?返回消息
?????*/

????private?String?message;
}

看到這里,有沒有眼前一亮的感覺,代碼示例中定義了兩個枚舉實例:BAD_LICENCE_TYPELICENCE_NOT_FOUND,分別對應(yīng)了BadLicenceTypeExceptionLicenceNotFoundException兩種異常。以后每增加一種異常情況,只需增加一個枚舉實例即可,再也不用每一種異常都定義一個異常類了。然后再來看下如何使用,假設(shè)LicenceService有校驗Licence是否存在的方法,如下:

/**
?*?校驗{@link?Licence}存在
?*?@param?licence
?*/

private?void?checkNotNull(Licence?licence)?{
??ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
}

若不使用斷言,代碼可能如下:

private?void?checkNotNull(Licence?licence)?{
??if?(licence?==?null)?{
????throw?new?LicenceNotFoundException();
????//?或者這樣
????throw?new?BusinessException(7001,?"Bad?licence?type.");
??}
}

使用枚舉類結(jié)合(繼承)Assert,只需根據(jù)特定的異常情況定義不同的枚舉實例,如上面的BAD_LICENCE_TYPE、LICENCE_NOT_FOUND,就能夠針對不同情況拋出特定的異常(這里指攜帶特定的異常碼和異常消息),這樣既不用定義大量的異常類,同時還具備了斷言的良好可讀性,當(dāng)然這種方案的好處遠(yuǎn)不止這些,請繼續(xù)閱讀后文,慢慢體會。

注:上面舉的例子是針對特定的業(yè)務(wù),而有部分異常情況是通用的,比如:服務(wù)器繁忙、網(wǎng)絡(luò)異常、服務(wù)器異常、參數(shù)校驗異常、404等,所以有CommonResponseEnumArgumentResponseEnum、ServletResponseEnum,其中 ServletResponseEnum 會在后文詳細(xì)說明。

定義統(tǒng)一異常處理器類

@Slf4j
@Component
@ControllerAdvice
@ConditionalOnWebApplication
@ConditionalOnMissingBean(UnifiedExceptionHandler.class)
public?class?UnifiedExceptionHandler?
{
????/**
?????*?生產(chǎn)環(huán)境
?????*/

????private?final?static?String?ENV_PROD?=?"prod";
?
????@Autowired
????private?UnifiedMessageSource?unifiedMessageSource;
?
????/**
?????*?當(dāng)前環(huán)境
?????*/

????@Value("${spring.profiles.active}")
????private?String?profile;
????
????/**
?????*?獲取國際化消息
?????*
?????*?@param?e?異常
?????*?@return
?????*/

????public?String?getMessage(BaseException?e)?{
????????String?code?=?"response."?+?e.getResponseEnum().toString();
????????String?message?=?unifiedMessageSource.getMessage(code,?e.getArgs());
?
????????if?(message?==?null?||?message.isEmpty())?{
????????????return?e.getMessage();
????????}
?
????????return?message;
????}
?
????/**
?????*?業(yè)務(wù)異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler(value?=?BusinessException.class)
????@ResponseBody
????public?ErrorResponse?handleBusinessException(BaseException?e)?
{
????????log.error(e.getMessage(),?e);
?
????????return?new?ErrorResponse(e.getResponseEnum().getCode(),?getMessage(e));
????}
?
????/**
?????*?自定義異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler(value?=?BaseException.class)
????@ResponseBody
????public?ErrorResponse?handleBaseException(BaseException?e)?
{
????????log.error(e.getMessage(),?e);
?
????????return?new?ErrorResponse(e.getResponseEnum().getCode(),?getMessage(e));
????}
?
????/**
?????*?Controller上一層相關(guān)異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler({
????????????NoHandlerFoundException.class,
????????????HttpRequestMethodNotSupportedException.class,
????????????HttpMediaTypeNotSupportedException.class,
????????????MissingPathVariableException.class,
????????????MissingServletRequestParameterException.class,
????????????TypeMismatchException.class,
????????????HttpMessageNotReadableException.class,
????????????HttpMessageNotWritableException.class,
????????????//?BindException.class,
????????????//?MethodArgumentNotValidException.class
????????????HttpMediaTypeNotAcceptableException.class,
????????????ServletRequestBindingException.class,
????????????ConversionNotSupportedException.class,
????????????MissingServletRequestPartException.class,
????????????AsyncRequestTimeoutException.class
????})
????@ResponseBody
????public?ErrorResponse?handleServletException(Exception?e)?
{
????????log.error(e.getMessage(),?e);
????????int?code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????try?{
????????????ServletResponseEnum?servletExceptionEnum?=?ServletResponseEnum.valueOf(e.getClass().getSimpleName());
????????????code?=?servletExceptionEnum.getCode();
????????}?catch?(IllegalArgumentException?e1)?{
????????????log.error("class?[{}]?not?defined?in?enum?{}",?e.getClass().getName(),?ServletResponseEnum.class.getName());
????????}
?
????????if?(ENV_PROD.equals(profile))?{
????????????//?當(dāng)為生產(chǎn)環(huán)境,?不適合把具體的異常信息展示給用戶,?比如404.
????????????code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????????BaseException?baseException?=?new?BaseException(CommonResponseEnum.SERVER_ERROR);
????????????String?message?=?getMessage(baseException);
????????????return?new?ErrorResponse(code,?message);
????????}
?
????????return?new?ErrorResponse(code,?e.getMessage());
????}
?
?
????/**
?????*?參數(shù)綁定異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler(value?=?BindException.class)
????@ResponseBody
????public?ErrorResponse?handleBindException(BindException?e)?
{
????????log.error("參數(shù)綁定校驗異常",?e);
?
????????return?wrapperBindingResult(e.getBindingResult());
????}
?
????/**
?????*?參數(shù)校驗異常,將校驗失敗的所有異常組合成一條錯誤信息
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler(value?=?MethodArgumentNotValidException.class)
????@ResponseBody
????public?ErrorResponse?handleValidException(MethodArgumentNotValidException?e)?
{
????????log.error("參數(shù)綁定校驗異常",?e);
?
????????return?wrapperBindingResult(e.getBindingResult());
????}
?
????/**
?????*?包裝綁定異常結(jié)果
?????*
?????*?@param?bindingResult?綁定結(jié)果
?????*?@return?異常結(jié)果
?????*/

????private?ErrorResponse?wrapperBindingResult(BindingResult?bindingResult)?{
????????StringBuilder?msg?=?new?StringBuilder();
?
????????for?(ObjectError?error?:?bindingResult.getAllErrors())?{
????????????msg.append(",?");
????????????if?(error?instanceof?FieldError)?{
????????????????msg.append(((FieldError)?error).getField()).append(":?");
????????????}
????????????msg.append(error.getDefaultMessage()?==?null???""?:?error.getDefaultMessage());
?
????????}
?
????????return?new?ErrorResponse(ArgumentResponseEnum.VALID_ERROR.getCode(),?msg.substring(2));
????}
?
????/**
?????*?未定義異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/

????@ExceptionHandler(value?=?Exception.class)
????@ResponseBody
????public?ErrorResponse?handleException(Exception?e)?
{
????????log.error(e.getMessage(),?e);
?
????????if?(ENV_PROD.equals(profile))?{
????????????//?當(dāng)為生產(chǎn)環(huán)境,?不適合把具體的異常信息展示給用戶,?比如數(shù)據(jù)庫異常信息.
????????????int?code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????????BaseException?baseException?=?new?BaseException(CommonResponseEnum.SERVER_ERROR);
????????????String?message?=?getMessage(baseException);
????????????return?new?ErrorResponse(code,?message);
????????}
?
????????return?new?ErrorResponse(CommonResponseEnum.SERVER_ERROR.getCode(),?e.getMessage());
????}
????
}

可以看到,上面將異常分成幾類,實際上只有兩大類,一類是ServletException、ServiceException,還記得上文提到的 按階段分類 嗎,即對應(yīng) 進(jìn)入Controller前的異常 和 Service 層異常;然后 ServiceException 再分成自定義異常、未知異常。對應(yīng)關(guān)系如下:

  • 進(jìn)入Controller前的異常: handleServletException、handleBindException、handleValidException
  • 自定義異常: handleBusinessException、handleBaseException
  • 未知異常: handleException

接下來分別對這幾種異常處理器做詳細(xì)說明。

異常處理器說明

handleServletException

一個http請求,在到達(dá)Controller前,會對該請求的請求信息與目標(biāo)控制器信息做一系列校驗。這里簡單說一下:

NoHandlerFoundException:首先根據(jù)請求Url查找有沒有對應(yīng)的控制器,若沒有則會拋該異常,也就是大家非常熟悉的404異常;

HttpRequestMethodNotSupportedException:若匹配到了(匹配結(jié)果是一個列表,不同的是http方法不同,如:Get、Post等),則嘗試將請求的http方法與列表的控制器做匹配,若沒有對應(yīng)http方法的控制器,則拋該異常;

HttpMediaTypeNotSupportedException:然后再對請求頭與控制器支持的做比較,比如content-type請求頭,若控制器的參數(shù)簽名包含注解@RequestBody,但是請求的content-type請求頭的值沒有包含application/json,那么會拋該異常(當(dāng)然,不止這種情況會拋這個異常);

MissingPathVariableException:未檢測到路徑參數(shù)。比如url為:/licence/{licenceId},參數(shù)簽名包含@PathVariable("licenceId"),當(dāng)請求的url為/licence,在沒有明確定義url為/licence的情況下,會被判定為:缺少路徑參數(shù);

MissingServletRequestParameterException:缺少請求參數(shù)。比如定義了參數(shù)@RequestParam("licenceId") String licenceId,但發(fā)起請求時,未攜帶該參數(shù),則會拋該異常;

TypeMismatchException: 參數(shù)類型匹配失敗。比如:接收參數(shù)為Long型,但傳入的值確是一個字符串,那么將會出現(xiàn)類型轉(zhuǎn)換失敗的情況,這時會拋該異常;

HttpMessageNotReadableException:與上面的HttpMediaTypeNotSupportedException舉的例子完全相反,即請求頭攜帶了"content-type: application/json;charset=UTF-8",但接收參數(shù)卻沒有添加注解@RequestBody,或者請求體攜帶的 json 串反序列化成 pojo 的過程中失敗了,也會拋該異常;

HttpMessageNotWritableException:返回的 pojo 在序列化成 json 過程失敗了,那么拋該異常;

handleBindException

參數(shù)校驗異常,后文詳細(xì)說明。

handleValidException

參數(shù)校驗異常,后文詳細(xì)說明。

handleBusinessException、handleBaseException

處理自定義的業(yè)務(wù)異常,只是handleBaseException處理的是除了 BusinessException 意外的所有業(yè)務(wù)異常。就目前來看,這2個是可以合并成一個的。

handleException

處理所有未知的異常,比如操作數(shù)據(jù)庫失敗的異常。

注:上面的handleServletExceptionhandleException 這兩個處理器,返回的異常信息,不同環(huán)境返回的可能不一樣,以為這些異常信息都是框架自帶的異常信息,一般都是英文的,不太好直接展示給用戶看,所以統(tǒng)一返回SERVER_ERROR代表的異常信息。

異于常人的404

上文提到,當(dāng)請求沒有匹配到控制器的情況下,會拋出NoHandlerFoundException異常,但其實默認(rèn)情況下不是這樣,默認(rèn)情況下會出現(xiàn)類似如下頁面:

e5f9207c330e34a3ea369b5785b2fb85.png

Whitelabel Error Page

這個頁面是如何出現(xiàn)的呢?實際上,當(dāng)出現(xiàn)404的時候,默認(rèn)是不拋異常的,而是 forward跳轉(zhuǎn)到/error控制器,spring也提供了默認(rèn)的error控制器,如下:

fa45573ef575a2ee9c754551729d953e.png

那么,如何讓404也拋出異常呢,只需在properties文件中加入如下配置即可:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

如此,就可以異常處理器中捕獲它了,然后前端只要捕獲到特定的狀態(tài)碼,立即跳轉(zhuǎn)到404頁面即可

捕獲404對應(yīng)的異常

統(tǒng)一返回結(jié)果

在驗證統(tǒng)一異常處理器之前,順便說一下統(tǒng)一返回結(jié)果。說白了,其實是統(tǒng)一一下返回結(jié)果的數(shù)據(jù)結(jié)構(gòu)。codemessage 是所有返回結(jié)果中必有的字段,而當(dāng)需要返回數(shù)據(jù)時,則需要另一個字段 data 來表示。

所以首先定義一個 BaseResponse 來作為所有返回結(jié)果的基類;

然后定義一個通用返回結(jié)果類CommonResponse,繼承 BaseResponse,而且多了字段 data;

為了區(qū)分成功和失敗返回結(jié)果,于是再定義一個 ErrorResponse

最后還有一種常見的返回結(jié)果,即返回的數(shù)據(jù)帶有分頁信息,因為這種接口比較常見,所以有必要單獨定義一個返回結(jié)果類 QueryDataResponse,該類繼承自 CommonResponse,只是把 data 字段的類型限制為 QueryDdata,QueryDdata中定義了分頁信息相應(yīng)的字段,即totalCountpageNo、 pageSize、records。

其中比較常用的只有 CommonResponseQueryDataResponse,但是名字又賊鬼死長,何不定義2個名字超簡單的類來替代呢?于是 RQR 誕生了,以后返回結(jié)果的時候只需這樣寫:new R<>(data)new QR<>(queryData)。

所有的返回結(jié)果類的定義這里就不貼出來了

驗證統(tǒng)一異常處理

因為這一套統(tǒng)一異常處理可以說是通用的,所有可以設(shè)計成一個 common包,以后每一個新項目/模塊只需引入該包即可。所以為了驗證,需要新建一個項目,并引入該 common包。

主要代碼

下面是用于驗證的主要源碼:

@Service
public?class?LicenceService?extends?ServiceImpl<LicenceMapper,?Licence>?{
?
????@Autowired
????private?OrganizationClient?organizationClient;
?
????/**
?????*?查詢{@link?Licence}?詳情
?????*?@param?licenceId
?????*?@return
?????*/

????public?LicenceDTO?queryDetail(Long?licenceId)?{
????????Licence?licence?=?this.getById(licenceId);
????????checkNotNull(licence);
?
????????OrganizationDTO?org?=?ClientUtil.execute(()?->?organizationClient.getOrganization(licence.getOrganizationId()));
????????return?toLicenceDTO(licence,?org);
????}
?
????/**
?????*?分頁獲取
?????*?@param?licenceParam?分頁查詢參數(shù)
?????*?@return
?????*/

????public?QueryData?getLicences(LicenceParam?licenceParam)?{
????????String?licenceType?=?licenceParam.getLicenceType();
????????LicenceTypeEnum?licenceTypeEnum?=?LicenceTypeEnum.parseOfNullable(licenceType);
????????//?斷言,?非空
????????ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum);
?
????????LambdaQueryWrapper?wrapper?=?new?LambdaQueryWrapper<>();
????????wrapper.eq(Licence::getLicenceType,?licenceType);
????????IPage?page?=?this.page(new?QueryPage<>(licenceParam),?wrapper);
????????return?new?QueryData<>(page,?this::toSimpleLicenceDTO);
????}
?
????/**
?????*?新增{@link?Licence}
?????*?@param?request?請求體
?????*?@return
?????*/

????@Transactional(rollbackFor?=?Throwable.class)
????public?LicenceAddRespData?addLicence(LicenceAddRequest?request)?
{
????????Licence?licence?=?new?Licence();
????????licence.setOrganizationId(request.getOrganizationId());
????????licence.setLicenceType(request.getLicenceType());
????????licence.setProductName(request.getProductName());
????????licence.setLicenceMax(request.getLicenceMax());
????????licence.setLicenceAllocated(request.getLicenceAllocated());
????????licence.setComment(request.getComment());
????????this.save(licence);
?
????????return?new?LicenceAddRespData(licence.getLicenceId());
????}
?
????/**
?????*?entity?->?simple?dto
?????*?@param?licence?{@link?Licence}?entity
?????*?@return?{@link?SimpleLicenceDTO}
?????*/

????private?SimpleLicenceDTO?toSimpleLicenceDTO(Licence?licence)?{
????????//?省略
????}
?
????/**
?????*?entity?->?dto
?????*?@param?licence?{@link?Licence}?entity
?????*?@param?org?{@link?OrganizationDTO}
?????*?@return?{@link?LicenceDTO}
?????*/

????private?LicenceDTO?toLicenceDTO(Licence?licence,?OrganizationDTO?org)?{
????????//?省略
????}
?
????/**
?????*?校驗{@link?Licence}存在
?????*?@param?licence
?????*/

????private?void?checkNotNull(Licence?licence)?{
????????ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
????}
?
}

PS: 這里使用的DAO框架是mybatis-plus。啟動時,自動插入的數(shù)據(jù)為:

--?licence
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(1,?1,?'user','CustomerPro',?100,5);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(2,?1,?'user','suitability-plus',?200,189);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(3,?2,?'user','HR-PowerSuite',?100,4);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(4,?2,?'core-prod','WildCat?Application?Gateway',?16,16);
?
--?organizations
INSERT?INTO?organization?(id,?name,?contact_name,?contact_email,?contact_phone)
VALUES?(1,?'customer-crm-co',?'Mark?Balster',?'[email protected]',?'823-555-1212');
INSERT?INTO?organization?(id,?name,?contact_name,?contact_email,?contact_phone)
VALUES?(2,?'HR-PowerSuite',?'Doug?Drewry','[email protected]',?'920-555-1212');
開始驗證
捕獲自定義異常

\1. 獲取不存在的 licence 詳情:http://localhost:10000/licence/5。成功響應(yīng)的請求:licenceId=1

檢驗非空

56a6241947f27bb85cc6e7520b245473.png

捕獲 Licence not found 異常

Licence not found

2. 根據(jù)不存在的 licence type 獲取 licence 列表:http://localhost:10000/licence/list?licenceType=ddd??蛇x的 licence type 為:user、core-prod 。

校驗非空

5ea3fe890757741f0da99584ecefc18c.png

捕獲 Bad licence type 異常

Bad licence type

捕獲進(jìn)入 Controller 前的異常

\1. 訪問不存在的接口:http://localhost:10000/licence/list/ddd

捕獲404異常

2c3da7c7fef17253fe6f64544dc8f725.png

\2. http 方法不支持:http://localhost:10000/licence

PostMapping

4f62ee6687b5d26f06aad6fac5062d8b.png

捕獲 Request method not supported 異常

6cd8df5924c267157c159792fed53275.png

Request method not supported

abef6682322a05730b8fc249998ecba6.png

\3. 校驗異常1:http://localhost:10000/licence/list?licenceType=

getLicences

c40d304d86c7bb1f3dc44e8ad142594c.png

LicenceParam

dceec34372c26d381e4247fb4259f357.png

捕獲參數(shù)綁定校驗異常

631a38f84f06aea8db472b8265ac021d.png

licence type cannot be empty

4. 校驗異常2:post 請求,這里使用postman模擬。

addLicence

a5fa87a66c19b00037d7f9c865c9831b.png

LicenceAddRequest

53dfcd3cdfeac2d8f5cad09d3f62fb5a.png

請求url即結(jié)果

ed908bb080098b3a821aa84b3539aa05.png

捕獲參數(shù)綁定校驗異常

注:因為參數(shù)綁定校驗異常的異常信息的獲取方式與其它異常不一樣,所以才把這2種情況的異常從 進(jìn)入 Controller 前的異常 單獨拆出來,下面是異常信息的收集邏輯:

異常信息的收集

1c84286d05eb6fbffedca7c4f03a7c57.png
捕獲未知異常

假設(shè)我們現(xiàn)在隨便對 Licence 新增一個字段 test,但不修改數(shù)據(jù)庫表結(jié)構(gòu),然后訪問:http://localhost:10000/licence/1。

增加test字段

b5858932c8f8ba7cb6a759c2f44c33c2.png

捕獲數(shù)據(jù)庫異常

16b663dab2c6ce8a4cb7b2ab2ccb9456.png

Error querying database

b69c03b0d194bd3e942a0a761d410d89.png

小結(jié)

可以看到,測試的異常都能夠被捕獲,然后以 code、message 的形式返回。每一個項目/模塊,在定義業(yè)務(wù)異常的時候,只需定義一個枚舉類,然后實現(xiàn)接口 BusinessExceptionAssert,最后為每一種業(yè)務(wù)異常定義對應(yīng)的枚舉實例即可,而不用定義許多異常類。使用的時候也很方便,用法類似斷言。

擴展

在生產(chǎn)環(huán)境,若捕獲到 未知異常 或者 ServletException,因為都是一長串的異常信息,若直接展示給用戶看,顯得不夠?qū)I(yè),于是,我們可以這樣做:當(dāng)檢測到當(dāng)前環(huán)境是生產(chǎn)環(huán)境,那么直接返回 "網(wǎng)絡(luò)異常"。

生產(chǎn)環(huán)境返回“網(wǎng)絡(luò)異?!?/p>

8a7550216acbb230b7da09af97e0f520.png

可以通過以下方式修改當(dāng)前環(huán)境:

4562ff0118acdb6df880b323b8c8f32e.png

修改當(dāng)前環(huán)境為生產(chǎn)環(huán)境

總結(jié)

使用 斷言枚舉類 相結(jié)合的方式,再配合統(tǒng)一異常處理,基本大部分的異常都能夠被捕獲。為什么說大部分異常,因為當(dāng)引入 spring cloud security 后,還會有認(rèn)證/授權(quán)異常,網(wǎng)關(guān)的服務(wù)降級異常、跨模塊調(diào)用異常、遠(yuǎn)程調(diào)用第三方服務(wù)異常等,這些異常的捕獲方式與本文介紹的不太一樣,不過限于篇幅,這里不做詳細(xì)說明,以后會有單獨的文章介紹。

另外,當(dāng)需要考慮國際化的時候,捕獲異常后的異常信息一般不能直接返回,需要轉(zhuǎn)換成對應(yīng)的語言,不過本文已考慮到了這個,獲取消息的時候已經(jīng)做了國際化映射,邏輯如下:

1aa62c2df8de31abd1bd9f361674c3de.png

獲取國際化消息

最后總結(jié),全局異常屬于老生長談的話題,希望這次通過手機的項目對大家有點指導(dǎo)性的學(xué)習(xí)。大家根據(jù)實際情況自行修改。

也可以采用以下的jsonResult對象的方式進(jìn)行處理,也貼出來代碼.

@Slf4j
@RestControllerAdvice
public?class?GlobalExceptionHandler?{
?
????/**
?????*?沒有登錄
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/

????@ExceptionHandler(NoLoginException.class)
????public?Object?noLoginExceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????
{
????????log.error("[GlobalExceptionHandler][noLoginExceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.NO_LOGIN);
????????jsonResult.setMessage("用戶登錄失效或者登錄超時,請先登錄");
????????return?jsonResult;
????}
?
????/**
?????*?業(yè)務(wù)異常
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/

????@ExceptionHandler(ServiceException.class)
????public?Object?businessExceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????
{
????????log.error("[GlobalExceptionHandler][businessExceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.FAILURE);
????????jsonResult.setMessage("業(yè)務(wù)異常,請聯(lián)系管理員");
????????return?jsonResult;
????}
?
????/**
?????*?全局異常處理
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/

????@ExceptionHandler(Exception.class)
????public?Object?exceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????
{
????????log.error("[GlobalExceptionHandler][exceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.FAILURE);
????????jsonResult.setMessage("系統(tǒng)錯誤,請聯(lián)系管理員");
????????return?jsonResult;
????}
}

我們創(chuàng)建了一個高質(zhì)量的技術(shù)交流群,與優(yōu)秀的人在一起,自己也會優(yōu)秀起來,趕緊點擊加群,享受一起成長的快樂。另外,如果你最近想跳槽的話,年前我花了2周時間收集了一波大廠面經(jīng),節(jié)后準(zhǔn)備跳槽的可以點擊這里領(lǐng)取!

推薦閱讀

··································

你好,我是程序猿DD,10年開發(fā)老司機、阿里云MVP、騰訊云TVP、出過書創(chuàng)過業(yè)、國企4年互聯(lián)網(wǎng)6年。從普通開發(fā)到架構(gòu)師、再到合伙人。一路過來,給我最深的感受就是一定要不斷學(xué)習(xí)并關(guān)注前沿。只要你能堅持下來,多思考、少抱怨、勤動手,就很容易實現(xiàn)彎道超車!所以,不要問我現(xiàn)在干什么是否來得及。如果你看好一個事情,一定是堅持了才能看到希望,而不是看到希望才去堅持。相信我,只要堅持下來,你一定比現(xiàn)在更好!如果你還沒什么方向,可以先關(guān)注我,這里會經(jīng)常分享一些前沿資訊,幫你積累彎道超車的資本。

點擊領(lǐng)取2022最新10000T學(xué)習(xí)資料
瀏覽 42
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 日韩中文字幕视频在线| 天天狠狠干| AV高清| 日韩肏逼| 大香蕉A片| 国产精品黄片| 欧美东京热视频| 婷婷V亚洲V丁香月天V日韩V | 吴梦梦《女教师时间暂停》| 一本色道精品久久一区二区三区 | 国产男人天堂| 国产av不卡| 无码日| 东方av在线观看| 97日韩天堂| 91久久精品国产91久久公交车| 亚洲中文字幕日韩精品| 欧美亚洲成人视频| 噜噜噜在线| 亚洲成人免费| 澳门av| 999热这里只有精品| 99国产一区| 岛国无码av| eeuss一区| 久久精品一区| 夜夜天天人人| 丰满人妻一区二区三区视频54| 尤物精品| 91黄色片| 国产一级在线免费观看| 老太婆擦BBBB撩BBBB| 91精品国产99久久久久久天美| 午夜视频福利| 伊人操逼网| 一级AV在线| 精东AV| 五月婷婷婷| 51色片| 澳门无码视频| 91免费成人| 国产三级网站| 三级毛片网站| 国产一级a毛一级a毛视频在线网站) | 69精品| 中文字幕在线播放av| 美女操B| 成人免费网站| 99九九99九九九99九他書對| 操BBB操BBB| 99久久99九九九99九他书对| 99精品热| 精品久久免费一区二区三区 | 广州媚黑妇系列视频在线| 国产毛片在线视频| 青青草原国产视频| 激情综合在线| 草在线视频| 欧美一级视频| 影音先锋AV天堂| 欧美午夜成人一区二区三区| 日本无码人妻| 精品交换一区二区三区无码| 99在线小视频| 91精品人妻少妇无码影院| 欧美日本亚洲| 91老熟| 日韩不卡在线观看| 嘿嘿午夜| 综合玖玖| a片视频网站| 国产免费操逼视频| 国产一级a毛片| 日本高清视频免费观看| 高清无码在线看| 蜜桃在线无码| 五月在线视频| 日韩人妻无码一区二区三区中文| 激情一级| 日逼天堂| 艹逼视频免费观看| 色欲综合网| AV中文字幕电影| 在线观看日韩av| 青娱乐大香蕉| 日韩欧美亚洲一区二区三区| 亚洲三级片在线视频| 夜夜爽日日爽| 国产网址| 91在线免费视频| av久操| 69视频网| 三级AV在线观看| 午夜无码av| 黄色性视频| 人成免费在线视频| 色男人的天堂| 五月婷婷精品| 人人草人人干| 亚洲精品国产精品国自产| 日韩一级网站| 精品素人在线| 一个色综合网| 色资源在线观看| 性日韩| 一区二区三区三级片| 日韩黄色视频网站| 国内综合久久| 少妇白洁在线观看| 亚洲AAA电影| 国产操美女| 黄色九九| 国产精品久久无码| 中文字幕黑人无码| 日韩aaaaaa| JlZZJLZZJlZZ亚洲女人17| 中文解说AⅤ水果派| 天天撸天天干天天日| 天堂一区在线观看| 欧美性极品少妇精品网站| 日本黄色电影在线| 成人片天天看片欧美一级| 天天日夜夜拍| 91超碰在线| 亚洲成人视频在线免费观看| 成人视频123| 91在线无码精品秘国产-百度| 天天干天天撸影视| 国产色综合视频| a在线观看免费| 亚洲欧洲av| 性欧美XXXX| 亚洲精品人伦一区二区| 日韩AV在线天堂| 特级西西人体444www高清大胆 | 男人亚洲天堂| 黄色毛片网| 一区二区三区四区| 日韩欧美视频在线播放| 五月停亭六月,六月停亭的英语| 色香蕉影院| 无码中文AV| a片免费网站| 久久精品国产精品| 黄片在线免费观看视频| 东京热视频一区| 自拍偷拍视频网址| 亚洲精品秘一区二区三区蜜桃久 | 国产毛片欧美毛片高潮| 国产精品黑人ThePorn| 91偷拍与自偷拍精品无码| 日本免费在线黄色视频| 中文字幕在线成人| 亚洲AV成人网| 日韩伊人网| 欧美色色色色色| 91丨熟女丨首页| 大香蕉视频网| 免费黄色电影在线观看| 美国操逼片| 国产精品一品二区三区的使用体验 | 欧美一区二区三区四| 黑人毛片91久久久久久| 国产乱伦一区| 婷婷精品国产一区二区三区日韩| 深爱五月激情网| A黄色片| 精品在线免费观看| 亚洲午夜久久久久久久久久久| 欧美美女视频网站| 久久午夜成人电影| 午夜私人福利| 久久精品秘一区二区三免费| 中文字幕在线免费看线人| 亚洲乱伦中文字幕| 91蜜桃在线| 久青草视频| 九九操比| 精品中文视频| 免费日比视频| 亚洲在线高清| 日韩无码黄片| 国产777777| 成人网肏逼视频| 男女性爱视频免费| 国产日逼片| 婷婷五月天大香蕉| 中文字幕乱码中文字幕电视剧| 粉粉嫩嫩的18虎白女| 日韩AV电影在线观看| 狠狠综合| 大黑逼AV| 中国一级片| 97人人色| 国产精品成人无码| 亚洲日韩AV在线| 国产精品18进进出出17c| 俺来也最新网址| 日韩一本道在线| 性爱一级| 欧美精品无码| 国产黄色免费| 91丨九色丨熟女老版| 精品国产香蕉| BBB搡BBB搡BBB搡BBB| 日韩AV一级| 性爱视频免费| 色婷婷大香蕉| 欧美操逼视频| 亚洲日韩成人AV| 熟女少妇一区二区| 91乱子伦国产乱| 日皮视频免费| 国产黄色电影| 日韩无码A级片| 狠狠色噜噜狠狠狠7777| 日本无码免费视频| 中日韩欧美一级A片免费| 四虎无码丰满人妻| 日逼高清无码| 91福利视频在线观看| 3D动漫啪啪精品一区二区中文字幕 | 亚洲无码一级视频| 欧美日P| 婷婷免费视频| 日本爱爱视频| 亚洲免费av在线| 北条麻妃AV在线播放| 壁特壁视频在线观看| 天天拍夜夜操| 91麻豆精品91久久久久同性| 久久大香蕉91| 国产毛片在线视频| 久久大香| 久99在线视频| 乱伦AV片| 92丨九色丨偷拍老熟女| 黄片www| 国产福利美女网站| 黄片大全在线免费观看| 色色毛片| 最新午夜综合福利视频| 欧一美一婬一伦一区二区三区| 日韩中文无码字幕| 色婷婷大香蕉| 精品中文字幕在线播放| 另类BBwBBw| 亚洲日本黄色视频| 大香蕉亚洲| 日韩一区二区免费看| 亚洲AV无码国产精品久久不卡| 日本翔田千里奶水| 精品人妻一区二区三区含羞草| 亚洲午夜福利一区二区三区| 欧美成人福利在线观看| 色婷婷AV一区二区三区软件| 99九九网| 亚洲性爱小说网址| 久久噜| 影音先锋久久| 国产亚洲婷婷| 二区三区免费视频| 免费a在线观看| 免费在线国产| 三级网站视频| 亚洲成人在线网站| 久久久无码精品亚洲日韩男男| 91久久精品日日躁夜夜躁欧美| 成人a片在线观看| 91视频导航| 双腿张开被9个男人调教| 亚洲AV性爱| 内射学生妹| 18禁成人A∨片| 三级片高清无码| 国产黄片在线免费观看| 欧美日韩一级在线观看| 人妻斩り43歳| 99在线视频免费观看| 色天堂色男人| 亚洲中文无码电影| 欧美日韩高清在线| 日韩高清精品在线| 91香蕉网| 在线无码一区| 日日夜夜超碰| 欧美日韩色| 国产夫妻在线| 国产一精品一aⅴ一免费| 香蕉AV777XXX色综合一区| 亚洲国产精品视频| 中文字幕一区二区三区人妻电影| 国产99久久九九精品无码免费| 国产免费无码一区二区| 香蕉在线播放| 人妻中文在线| 奇米影视色偷偷| 精品一区三区| 日韩极品视频| 成人无码高清在线观看| 欧美激情视频一区二区| 青青草超碰| 日逼大香蕉| 91在线无码精品秘国产| 日本三级片视频不卡| 国产九九九视频| 打炮影院| 99久久久久久久无码| 亚洲中文久久| 操东北女人| 少妇高潮日韩| 内射国产| 国产寡妇亲子伦一区二区三区四区 | 国产成人h| 67194熟女| 色淫视频| 国产操逼的视频| 亚洲精品视频在线| 操操操综合| 97人人爽人人爽人人人| 日韩av中文字幕在线播放| 先锋影音av资源站| 人妻视频网| 精品交换一区二区三区无码| 欧美日韩国产成人在线| 中文字幕av在线| A片啪啪| 中文丰满亲子伦| 91综合在线观看| 亚洲无码成人在线观看| 日韩免费在线视频观看| 国产精品第一| 日韩精品视频在线| 美女一级A片| 婷婷伊人| 东北骚妇大战黑人视频| 69视频国产| 夜夜嗨av无码一区二区三区 | 国产色情网站| 丁香五月婷婷视频| 九九热re99re6在线精品| 韩国高清无码视频| 三级片欧美| 狠狠爱av| 婷婷久热| 亚洲天堂视频在线播放| 婷婷五月天AV| 九久久| 97精品综合久久| 一级a一级a爱片兔兔软件| 免费一级A片在线播放| 91在线无码精品国产三年| 黄片视频免费播放| 黄色三级网站| 亚洲中文无码av| 国产一卡二卡三卡| 国产成人V在线精品一区| 久久一二三区| 成人黄色一级| 中文字幕日本在线| 手机AV免费| 91爱搞| 免费日韩AV| 青青草性爱| 1024在线视频| 免费无码婬片AAAA片在线蜜芽 | 韩国高清无码60.70.80| 午夜麻豆| 久久丁香五月婷婷五月天激情视频| 91人人人人| 大香蕉一区| 日韩三级片在线视频| 在线免费看黄色| 人妻精品无码| 精品无码产区一区二| 久久免费视频6| 狠狠干| 国产成人午夜精品无码区久久麻豆 | 91丨豆花丨国产极品| 久久久久久久91| 午夜av免费| 色中色av| 久久精品大香蕉| AV女优天堂| 四色永久成人网站| 激情男人网| 国产足交视频| 97色色婷婷五月天| 就要干就要操| 波多野结衣一区二区| 先锋影音资源AV| 91国产做爱| jzzijzzij亚洲成熟少妇在线播放| 亚洲AV中文无码| 成人视频网站在线观看| 国产熟妇搡BBBB搡BBBB搡| 丁香五月婷婷啪啪| 亚洲黄色av| va婷婷在线免费观看| 免费日韩| 亚洲无码午夜| 超碰一级片| 18禁网址| 大地资源第三页在线观看免费播放最新| 爆乳乱伦| 免费观看无码视频| 亚洲精品高清视频| 国产免费小视频| 996热re视频精品视频| 欧美激情一区二区| 国产小视频在线看| 无码电影网| 国产一级a一片成人AV| 日本高清一区二区高清免费视频 | 中文字幕三级片在线观看| 日本高清不卡视频| 亚洲午夜福利一区二区三区| 久久国产精品久久| 操逼999| 亚洲无线观看| 艹逼视频在线观看| 欧美专区一区| 色欲av伊人久久大香线蕉影院| 操B图| 插插视频| AV乱伦小说| AA级黄色视频| 欧美熟妇精品黑人巨大一二三区| a天堂在线| 大香蕉尹人网| 一级黄色在线| 久久午夜无码鲁丝午夜精品| 97A片在线观看播放| 黄色片久久| 亚洲日韩一区| caopeng97| 欧美成人午夜影院| 人人妻人人澡人人爽人人欧美一区| 天天撸在线| 三级片在线看片AV| 草逼视频网| 成年网站| 成人无码区免费A片久久鸭| 91精品在线免费观看| 人妻在线无码| 狠狠操AV| 精品国产久久久久久| 无码在线视频免费观看| 99久久国| 久久精彩免费视频| 激情五月天婷婷| 国产日韩欧美一区二区| 人人摸人人操人人看| 亚洲无码激情| 成人午夜小视频| 欧美国产日韩视频| 五月激情视频| 日韩人妻码一区二区三区| 国产特级婬片免费看| 国产免费AV片| 91人妻人人澡人人爽人人爽| 国产麻豆精品ThePorn| 国产视频无码| 99精品视频16在线免费观看| 日韩在线视频网| 亚洲色欧美| 一本到免费视频| 狠狠大香蕉| 麻豆mdapp01.tⅴ| 国产成人免费看| 俺也去俺也来| 久久久精品国产| 国产精品污www在线观看| 久久99精品久久久久| 9I成人免费版| 国产精品免费人成人网站酒店| 狼人综合视频| 黄片av| 国产无码中文字幕| 97干在线| 亚洲丁香五月激情| 日韩在线综合| 国产裸体网站| 亚洲日韩精品欧美一区二区yw | 51一区二区三区| 亚洲色图综合| a级网站| 国产主播一区二区| 亚洲中文在线观看| 久久噜噜噜精品国产亚洲综合| 国产乱论视频| 婷婷天天干| 国产AV一级| 又大又粗又爽| 中国a一片一级一片| 北条麻妃一区二区三区-免费免费高清观看| 97人妻人人澡人人爽人人| 婷婷丁香人妻天天爽| 成人乱妇无码AV在线| 婷婷丁香五月亚洲| 日本日逼网| av中文在线| 日本丰满老熟妇乱子伦| 久久精品久| 天天综合网久久| 成人精品在线| 亚洲v欧美| 超碰天堂| 亚洲国产精品自| AV解说| 日本综合久久| 一级免费片| 人妻懂色av粉嫩av浪潮av| 中文字幕综合网| 大伊人久久| 青青草伊人大香蕉| 亚洲国产精品18久久久久久| 国产精品久久777777| 日逼电影网| 青青草成人在线观看| 亚洲精品色婷婷| 午夜毛片| 91探花国产综合在线精品| 中文在线免费看视频| 蜜桃一区| 日本少妇高清视频| 欧美性爱中文字幕| 狠狠干中文字幕| 婷婷69| 国产免费高清无码| 一区二区三区四区无码| 日韩超碰在线| 午夜成人在线视频| 红桃91人妻爽人妻爽| 午夜视频99| 高清无码在线看| 国精产品一品二品国精| 日韩欧美在线中文字幕| 日韩精品一区二区三区在线观看免费 | 婷婷五月天综合网| 成人国产在线无码AV免费| 免费看无码| 日韩v欧美v日本v亚洲v国产v| 婷婷五月天在线观看| 丰满少妇在线观看网站| 婷婷五月精品| 日韩群交视频| 99久久国产视频| 影音先锋人妻限定| 欧美草逼视频| 婷婷五月天综合| www污| 日韩黄色片在线观看| 美女啪啪网站| 午夜福利无码视频| 人人操人人撸| 久久蝌蚪窝| 人人射在线| 国产在线你懂得| 青娱乐大香蕉| 无码中文字幕网站| 精品成人免费视频| 亚洲操逼电影| 日韩不卡中文字幕| 边吃奶边做爱| 黄色一区二区三区| 日韩精品成人电影| 精品一区二区久久久久久久网站| 影音先锋乱伦| 国产精品秘国产精品88| 国产高清精品无码| 白洁91视频| 亚洲中文字幕网| 人人综合| 午夜男人天堂| 亚洲天堂大香蕉| 人人操人人爱人人妻| 亚洲一级av无码毛片精品| 中文字幕免费在线观看| 亚洲一卡二卡| 中文字幕在线观看网址最新地址| 日韩一级片免费观看| 日韩久久精品视频| 亚洲无码一区二区三| 大香蕉精品欧美色综合2025| 亚洲日韩精品秘在线观看| 2021无码| 91亚洲国产AⅤ精品一区二区| 日本中文字幕免费| 精品黄色视频| 成人性爱视频在线| 欧美怕怕| 成人三级电影网| 超碰人人在线| 婷婷五月天激情小说| 丰满人妻一区二区三区蜜桃视频 | 一级黄色视频在线观看| 一区二区水蜜桃| 国产操逼免费视频| 色色色色综合| 日韩日批| 人人操97| 国产精品无码一区二区三| 国精品无码人妻一区二区三区免费| 91精品无码| 91丨九色丨熟女老版| 超碰啪啪| 丰满人妻一区二区| 国产精品成人无码专区| 国产又爽又黄免费网站在| 日韩一区二区高清无码| 天天射中文| 毛片学生妹| 俺来也俺去啦欧美www| 人人妻人人操人人爽| 免费激情网站| 日本久久电影| 免费AV片| 可以免费看的av| 日本天堂在线| 久久免费视频1| 日本精品黄色视频| 色色色色综合| 激情丁香五月婷婷| 丁香六月激情婷婷| 91丨熟女丨露脸| 中字幕视频在线永久在线观看免费 | 骚逼日本| 免费的黄色视频网站| 9l视频自拍蝌蚪9l视频成人| 国内自拍2025| 俺来也俺去也www色| 欧美footjob高跟脚交| 中文久久| 操操影院| 中文字幕AV在线播放| 日韩欧美中文字幕在线观看| 伊人自拍| 亚洲视频天堂| 天天添天天操| 人人干人人干人人| 四虎高清无码| 亚洲人成无码| 91调教视频| 美女黄色免费网站| 综合AV| 欧美日韩国产成人在线| 日韩中文在线播放| 在线日韩AV| 永久免费黄色视频| 亚洲v在线观看| 大香蕉在线75| 精品www| 新版欧美内射大全| 久久精品国产亚洲AV成人婷婷| 撸一撸在线观看| 成人伊人电影| 午夜福利AV在线| 国产九九九九九九| 不卡视频一区| 婷婷五月成人| 久久夜色精品国产噜噜亚洲AV| 色婷婷久综合久久一本国产AV | 国产在线观看免费成人视频| 欧美第一页| 91在线视频| 三级片AAA成人免费| 色我影院| 正在播放李彩斐被洋老外| 日本一级按摩片免费观看| 韩剧《邻居的妻子》电视剧| 淫荡五月天视频导航| 中文字幕在线观看福利视频| 影音先锋av在线资源站| 午夜无码在线观看视频| 国产免费成人| 汇聚全球淫荡熟女| 久久久97精品久久| 麻豆videos| 六月天av| 色哟哟视频在线观看| 黄色网址在线观看视频| 国产成人黄色片| 欧美大屌网站| 大肉大捧视频免费观看| 成年人视频网| 美女做爱视频网站| 国产麻豆精品成人免费视频| 俺来也av| 婷婷丁香五月亚洲| 俺去俺来也WWW色老板| 色中色在线视频| 日韩色道| 18岁毛片| 国产精品国产精品国产专区不片 | 国产欧美日本视频| 国产亚洲99久久精品| 亚洲A片免费看| 国产日韩欧美成人| 亚洲免费视频网站| 啊v视频在线| 激情操逼视频| 西西特级WWW444无码| 成年人在线观看视频网站| 人妻av中文字幕| 亚洲国产激情| 亚洲天天| 91站街农村熟女露脸| 婷婷操逼| 高清中字无码| 四虎色情| 日本三级片中文字幕| 久久久久久久久毛片| 在线中文字幕亚洲| 国內精品久久久久久久| 一区二区三区四区五区无码| www.午夜| 麻豆国产一区二区三区四区| 无码福利| 久热精品视频| 抽插视频欧美| 五月激情黄色| 欧美一区二区三区成人| 亚洲AV女人18毛片水真多| 五月六月婷婷| 国产精品免费观看久久久久久久久| 黄色小说视频网站| 久大香蕉| 青青草无码成人天堂免费| 日本在线| aaa少妇| 天天操夜夜操视频免费高清| 国产一区在线观看视频| 操美女久久| 大香蕉大香蕉视频网| 国产精品婷婷午夜在线观看| 天堂在线中文| 国产无码久久| 欧美成人A片| 久久精品色| 久久六六| 精品精品精品| 97久久一区二区| JLZZJLZZ亚洲女人| 午夜精品18视频国产17c| 内射免费视频| 长泽梓黑人初解禁BDD07| 欧美亚洲小说| 嫩BBB搡BBBB搡BBBB-百度| 先锋影音资源AV| av岛国免费| 在线a视频| 欧美色小说| 国产主播av| 黄色大片免费网站| 香蕉视频久久| 久操视频在线观看免费| 国产乱国产乱300精品| 国内特级毛片| 五月天婷婷在线无码| 成人亚洲网| 深夜福利一区二区| 91狠狠色丁香婷婷综合久久精品| 国产主播在线播放| 在线无码一区| 搞黄免费视频视频| 超碰最新在线观看| 无码三级av| 人人色人人操人人干| 精品乱子伦一区二区三区免费播放 | 肏少妇女情人大骚逼直播一区二区| 一道本视频在线免费观看| 国产成人h| 在线免费看黄网站| www天天干| 大香伊人| 蜜桃av秘无码一区二区三| 嘿嘿av| 中文字幕黄色| 黄片网站在线看| 天堂在线免费视频| 久久99久久99久久| 一区二区国产视频| 日韩成人免费在线观看| 国产成人在线免费| 男人的天堂黄色| 2015中文字幕黄色视频| 91精品国产综合久久久蜜臀粉嫩 | 97人妻人人揉人人躁人人| 久久精品福利视频| 国产精品色情A级片| 午夜视频18| 性爱一级片| 一曲二曲三曲在线观看中文字| 91狠狠综合久久| 亚洲午夜无码精品专区| 午夜老司机福利一二三区| 无码内射在线播放| A黄色片| 青春草视频| 五香丁香天堂网| 69Av视频| 亚洲国产激情| 少妇一区二区三区| 国产黄色片网站| 欧美成人免费在线| 国产中文在线观看| 精品国产国产没封| 亚洲无码激情在线| 欧美大黄视频| 亚洲色图欧美另类| 欧美日韩91| 国产精品久久久久久久免牛肉蒲 | 夜夜艹| 日本www色| 亚洲高清无码免费在线观看| 婷婷无码成人精品俺来俺去 | 国产TS丝袜人妖系列视频| 成人激情视频A极| 国精品无码一区二区三区在线秋菊 | 夜夜嗨AV| 国产91在线观看| 国产超碰在线| 人人草人人看人人摸| 日本啪啪网站| 91免费成人| 日韩人妻丰满无码区A片| 丰满的人妻一区二区10| 波多野结衣一二三区| 黄片视频在线免费观看| 亚洲成人AV| 色婷婷AV在线| 天堂网在线观看| 一级黄色电影网站| 天堂黄片| 亚洲高清电影| 天天日夜夜艹| 日韩一级在线观看| 成人国产无码| 日本中文字幕不卡| 日韩免费一级片| 亚洲人妻免费视频| 超碰天堂| 国产精品成人无码| 91大神在线免费看| 国产性爱免费视频| 精品国产污污免费网站入口| 五月婷婷综合网| 91操操操| 久久国产精品一区二区三区| 插菊花综合网2| 中国女人如毛片| аⅴ资源新版在线天堂| 欧美三级欧美成人高清| 人人操人人爽| www.色色网| 中文字幕免费高清网站| 亚洲婷婷五月| 黃色毛片A片AAAA级20| 婷婷五月精品| 先锋影音在线资源| 亚洲五月六月| 亚洲激情欧美| 欧美级毛片一进一出| 亚洲成人人妻| 99在线视频免费| 天天天操| 人人艹人人艹| 欧美人妻日韩精品| 特黄网站| 加勒比综合无码| www.狠狠爱| 日本Sm/调教/捆绑/紧缚| 国产波霸爆乳一区二区| 亚州成人视频| 欧美成人综合| 走光无码一区二区三区| 午夜福利免费| 影音先锋一区| 超碰97人妻| 亚洲AV无码乱码精| 东京热综合影院| 欧美午夜性爱视频| 亚洲精品成a人在线观看| 长泽梓黑人初解禁BDD07| 日本69AV| 嘿嘿午夜| 国产热视频| 色情一级AA片免费观看| 天天看天天操| 国产无限资源| 久久久无码视频| 中文字幕浅井香舞被黑人俘虏| 精品丰满人妻一区二区三区免费观| 一本大道DVD中文字幕| 美女黄色视频永费在线观看网站| 真实白嫖91探花无码| 欧美日韩中文字幕在线| 北条麻妃在线精品| 丁香六月综合激情|