Java中Validated、Valid 、Validator區(qū)別詳解
1. 結(jié)論先出
Valid VS Validated 相同點(diǎn)
都可以對(duì)方法和參數(shù)進(jìn)行校驗(yàn)
@Valid和@Validated
兩種注釋都會(huì)導(dǎo)致應(yīng)用標(biāo)準(zhǔn)Bean驗(yàn)證。
如果驗(yàn)證不通過(guò)會(huì)拋出BindException異常,并變成400(BAD_REQUEST)響應(yīng);或者可以通過(guò)Errors或BindingResult參數(shù)在控制器內(nèi)本地處理驗(yàn)證錯(cuò)誤。另外,如果參數(shù)前有@RequestBody注解,驗(yàn)證錯(cuò)誤會(huì)拋出MethodArgumentNotValidException異常。
JSR 380
JSR 380 是用于 bean 驗(yàn)證的 Java API 規(guī)范,是 Jakarta EE 和 JavaSE 的一部分。這確保 bean 的屬性滿足特定條件,使用諸如@NotNull、@Min和@Max 之類的注釋。
此版本需要 Java 8 或更高版本,并利用 Java 8 中添加的新功能,例如類型注釋和對(duì)Optional和LocalDate等新類型的支持。
有關(guān)規(guī)范的完整信息,請(qǐng)繼續(xù)閱讀JSR 380。
Valid VS Validated 不同點(diǎn)?
javax.validation.Valid
- 是JSR-303規(guī)范標(biāo)準(zhǔn)注解支持,是一個(gè)標(biāo)記注解。
- 注解支持ElementType#METHOD,ElementType#FIELD, ElementType#CONSTRUCTOR,
- ElementType#PARAMETER, ElementType#TYPE_USE
org.springframework.validation.annotation.Validated
- 是Spring 做得一個(gè)自定義注解,增強(qiáng)了分組功能。
- 注解支持 ElementType#TYPE,ElementType#METHOD,ElementType#PARAMETER
@Valid和@Validated區(qū)別
區(qū)別 | @Valid | @Validated |
---|---|---|
提供者 | JSR-303規(guī)范 | Spring |
是否支持分組 | 不支持 | 支持 |
標(biāo)注位置 | METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE | TYPE, METHOD, PARAMETER |
嵌套校驗(yàn) | 支持 | 不支持 |
Validator
Bean Validation 2.0(JSR 380)定義了用于實(shí)體和方法驗(yàn)證的元數(shù)據(jù)模型和API,Hibernate Validator是目前最好的實(shí)現(xiàn)
Validator接口有三個(gè)方法,可用于驗(yàn)證整個(gè)實(shí)體或僅驗(yàn)證實(shí)體的單個(gè)屬性
- Validator#validate() 驗(yàn)證所有bean的所有約束
- Validator#validateProperty() 驗(yàn)證單個(gè)屬性
- Validator#validateValue() 檢查給定類的單個(gè)屬性是否可以成功驗(yàn)證
不管是requestBody參數(shù)校驗(yàn)還是方法級(jí)別的校驗(yàn),最終都是調(diào)用Hibernate Validator執(zhí)行校驗(yàn),Spring Validation只是做了一層封裝。
驗(yàn)證用戶的輸入是我們大多數(shù)應(yīng)用程序中的常見(jiàn)功能。在 Java 生態(tài)系統(tǒng)中,我們專門使用Java Standard Bean Validation API來(lái)支持這一點(diǎn)。此外,從 4.0 版本開(kāi)始,這也與 Spring 很好地集成在一起.
在接下來(lái)的部分中,讓我們?cè)敿?xì)了解它們。
2. @Valid和@Validated 注解
在 Spring 中,我們使用 JSR-303 的@Valid注釋進(jìn)行方法級(jí)別驗(yàn)證。此外,我們還使用它來(lái)標(biāo)記成員屬性以進(jìn)行驗(yàn)證。但是,此注釋不支持組驗(yàn)證
。
組有助于限制驗(yàn)證期間應(yīng)用的約束。一個(gè)特殊的用例是 UI 界面(UI wizards)。在這里,在第一步中,我們可能有某個(gè)字段子組。在后續(xù)步驟中,可能有另一個(gè)組屬于同一個(gè) bean。因此我們需要在每一步中對(duì)這些有限的字段應(yīng)用約束,但@Valid不支持這一點(diǎn)。
在這種情況下,對(duì)于組級(jí)別,我們必須使用 Spring 的@Validated,它是 JSR-303 的@Valid的變體。這是在方法級(jí)別使用的。對(duì)于標(biāo)記成員屬性,我們繼續(xù)使用@Valid注釋。
現(xiàn)在,讓我們直接進(jìn)入并通過(guò)一個(gè)例子來(lái)看看這些注解的用法。
3. 例子
讓我們考慮一個(gè)使用 Spring Boot 開(kāi)發(fā)的簡(jiǎn)單用戶注冊(cè)。首先,我們將只有名稱和密碼屬性:
public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; // standard constructors / setters / getters / toString }
接下來(lái),讓我們看看控制器。在這里,我們將使用帶有@Valid注釋的saveBasicInfo方法來(lái)驗(yàn)證用戶輸入:
@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST) public String saveBasicInfo( @Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }
現(xiàn)在讓我們測(cè)試這個(gè)方法:
@Test public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }
確認(rèn)測(cè)試運(yùn)行成功后,我們現(xiàn)在擴(kuò)展功能。下一個(gè)合乎邏輯的步驟是將其轉(zhuǎn)換為復(fù)雜用戶注冊(cè)。第一步,名稱和密碼保持不變。在第二步中,我們將獲取諸如年齡 和 電話之類的附加信息。因此,我們將使用這些附加字段更新我們的域?qū)ο螅?nbsp;
public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; @Min(value = 18, message = "Age should not be less than 18") private int age; @NotBlank private String phone; // standard constructors / setters / getters / toString }
但是,這一次我們會(huì)注意到之前的測(cè)試失敗了。這是因?yàn)槲覀儧](méi)有傳入age和phone字段。為了支持這種行為,我們需要組驗(yàn)證和@Validated注釋。
為此,我們需要對(duì)字段進(jìn)行分組,創(chuàng)建兩個(gè)不同的組。首先,我們需要?jiǎng)?chuàng)建兩個(gè)標(biāo)記接口。每個(gè)組或每個(gè)步驟單獨(dú)一個(gè)。我們可以參考我們關(guān)于組驗(yàn)證的文章以了解具體的實(shí)現(xiàn)方式。在這里,讓我們關(guān)注注釋的差異。
我們將有第一步的BasicInfo接口和第二步的 AdvanceInfo 。此外,我們將更新UserAccount類以使用這些標(biāo)記接口,如下所示:
public class UserAccount { @NotNull(groups = BasicInfo.class) @Size(min = 4, max = 15, groups = BasicInfo.class) private String password; @NotBlank(groups = BasicInfo.class) private String name; @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class) private int age; @NotBlank(groups = AdvanceInfo.class) private String phone; // standard constructors / setters / getters / toString }
此外,我們現(xiàn)在將更新我們的控制器以使用@Validated批注而不是@Valid:
@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST) public String saveBasicInfoStep1( @Validated(BasicInfo.class) @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }
由于此更新,我們的測(cè)試現(xiàn)在成功運(yùn)行?,F(xiàn)在讓我們也測(cè)試一下這個(gè)新方法:
@Test public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }
這也運(yùn)行成功。因此,我們可以看到@Validated的使用 對(duì)于組驗(yàn)證至關(guān)重要。
接下來(lái),讓我們看看@Valid如何觸發(fā)嵌套屬性的驗(yàn)證。
4.使用@Valid嵌套校驗(yàn)
@Valid注釋用于校驗(yàn)嵌套屬性。這會(huì)觸發(fā)嵌套對(duì)象的驗(yàn)證。例如,在我們當(dāng)前的場(chǎng)景中,讓我們創(chuàng)建一個(gè) UserAddress 對(duì)象:
public class UserAddress { @NotBlank private String countryCode; // standard constructors / setters / getters / toString }
為了確保此嵌套對(duì)象的驗(yàn)證,我們將使用@Valid注釋來(lái)裝飾該屬性:
public class UserAccount { //... @Valid @NotNull(groups = AdvanceInfo.class) private UserAddress useraddress; // standard constructors / setters / getters / toString }
5. 組合使用@Valid和@Validated 進(jìn)行集合校驗(yàn)
如果請(qǐng)求體直接傳遞了json數(shù)組給后臺(tái),并希望對(duì)數(shù)組中的每一項(xiàng)都進(jìn)行參數(shù)校驗(yàn)。此時(shí),如果我們直接使用java.util.Collection下的list或者set來(lái)接收數(shù)據(jù),參數(shù)校驗(yàn)并不會(huì)生效!我們可以使用自定義list集合來(lái)接收參數(shù):
包裝List類型,并聲明@Valid注解
package com.devicemag.core.BO; import javax.validation.Valid; import java.util.*; /** * @Title: 參數(shù)校驗(yàn)工具類, 用于校驗(yàn)List<E> 類型的請(qǐng)求參數(shù) * @ClassName: com.devicemag.core.BO.ValidList.java * @Description: * * @Copyright 2020-2021 - Powered By 研發(fā)中心 * @author: 王延飛 * @date: 2020/12/25 20:23 * @version V1.0 */ public class ValidList<E> implements List<E> { @Valid private List<E> list = new ArrayList<>(); @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @Override public Iterator<E> iterator() { return list.iterator(); } @Override public Object[] toArray() { return list.toArray(); } @Override public <T> T[] toArray(T[] a) { return list.toArray(a); } @Override public boolean add(E e) { return list.add(e); } @Override public boolean remove(Object o) { return list.remove(o); } @Override public boolean containsAll(Collection<?> c) { return list.containsAll(c); } @Override public boolean addAll(Collection<? extends E> c) { return list.addAll(c); } @Override public boolean addAll(int index, Collection<? extends E> c) { return list.addAll(index, c); } @Override public boolean removeAll(Collection<?> c) { return list.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return list.retainAll(c); } @Override public void clear() { list.clear(); } @Override public E get(int index) { return list.get(index); } @Override public E set(int index, E element) { return list.set(index, element); } @Override public void add(int index, E element) { list.add(index, element); } @Override public E remove(int index) { return list.remove(index); } @Override public int indexOf(Object o) { return list.indexOf(o); } @Override public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @Override public ListIterator<E> listIterator() { return list.listIterator(); } @Override public ListIterator<E> listIterator(int index) { return list.listIterator(index); } @Override public List<E> subList(int fromIndex, int toIndex) { return list.subList(fromIndex, toIndex); } public List<E> getList() { return list; } public void setList(List<E> list) { this.list = list; } // 一定要記得重寫(xiě)toString方法 @Override public String toString() { return "ValidList{" + "list=" + list + '}'; } }
比如,我們需要一次性保存多個(gè)UserAccount 對(duì)象,Controller層的方法可以這么寫(xiě):
@PostMapping("/saveList") public Result saveList(@RequestBody @Validated(UserAccount.class) ValidationList<UserAccount > userList) { // 校驗(yàn)通過(guò),才會(huì)執(zhí)行業(yè)務(wù)邏輯處理 return Result.ok(); }
6. 自定義校驗(yàn)
validator-api-2.0的約束注解有22個(gè),具體我們看下面表格
空與非空檢查
注解 | 支持Java類型 | 說(shuō)明 |
---|---|---|
@Null | Object | 為null |
@NotNull | Object | 不為null |
@NotBlank | CharSequence | 不為null,且必須有一個(gè)非空格字符 |
@NotEmpty | CharSequence、Collection、Map、Array | 不為null,且不為空(length/size>0) |
Boolean值檢查
注解 | 支持Java類型 | 說(shuō)明 | 備注 |
---|---|---|---|
@AssertTrue | boolean、Boolean | 為true | 為null有效 |
@AssertFalse | boolean、Boolean | 為false | 為null有效 |
日期檢查
注解 | 支持Java類型 | 說(shuō)明 | 備注 |
---|---|---|---|
@Future | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 驗(yàn)證日期為當(dāng)前時(shí)間之后 | 為null有效 |
@FutureOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 驗(yàn)證日期為當(dāng)前時(shí)間或之后 | 為null有效 |
@Past | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 驗(yàn)證日期為當(dāng)前時(shí)間之前 | 為null有效 |
@PastOrPresent | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 驗(yàn)證日期為當(dāng)前時(shí)間或之前 | 為null有效 |
數(shù)值檢查
注解 | 支持Java類型 | 說(shuō)明 | 備注 |
---|---|---|---|
@Max | BigDecimal、BigInteger,byte、short、int、long以及包裝類 | 小于或等于 | 為null有效 |
@Min | BigDecimal、BigInteger,byte、short、int、long以及包裝類 | 大于或等于 | 為null有效 |
@DecimalMax | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包裝類 | 小于或等于 | 為null有效 |
@DecimalMin | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包裝類 | 大于或等于 | 為null有效 |
@Negative | BigDecimal、BigInteger,byte、short、int、long、float、double以及包裝類 | 負(fù)數(shù) | 為null有效,0無(wú)效 |
@NegativeOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包裝類 | 負(fù)數(shù)或零 | 為null有效 |
@Positive | BigDecimal、BigInteger,byte、short、int、long、float、double以及包裝類 | 正數(shù) | 為null有效,0無(wú)效 |
@PositiveOrZero | BigDecimal、BigInteger,byte、short、int、long、float、double以及包裝類 | 正數(shù)或零 | 為null有效 |
@Digits(integer = 3, fraction = 2) | BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包裝類 | 整數(shù)位數(shù)和小數(shù)位數(shù)上限 | 為null有效 |
其他
注解 | 支持Java類型 | 說(shuō)明 | 備注 |
---|---|---|---|
@Pattern | CharSequence | 匹配指定的正則表達(dá)式 | 為null有效 |
CharSequence | 郵箱地址 | 為null有效,默認(rèn)正則 '.*' |
|
@Size | CharSequence、Collection、Map、Array | 大小范圍(length/size>0) | 為null有效 |
hibernate-validator擴(kuò)展約束(部分)
注解 | 支持Java類型 | 說(shuō)明 |
---|---|---|
@Length | String | 字符串長(zhǎng)度范圍 |
@Range | 數(shù)值類型和String | 指定范圍 |
@URL | URL地址驗(yàn)證 |
自定義約束注解
除了以上提供的約束注解(大部分情況都是能夠滿足的),我們還可以根據(jù)自己的需求自定義自己的約束注解
定義自定義約束,有三個(gè)步驟
- 創(chuàng)建約束注解
- 實(shí)現(xiàn)一個(gè)驗(yàn)證器
- 定義默認(rèn)的錯(cuò)誤信息
那么下面就直接來(lái)定義一個(gè)簡(jiǎn)單的驗(yàn)證手機(jī)號(hào)碼的注解
@Documented @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Constraint(validatedBy = {MobileValidator.class}) @Retention(RUNTIME) @Repeatable(Mobile.List.class) public @interface Mobile { /** * 錯(cuò)誤提示信息,可以寫(xiě)死,也可以填寫(xiě)國(guó)際化的key */ String message() default "手機(jī)號(hào)碼不正確"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$"; @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @interface List { Mobile[] value(); } }
關(guān)于注解的配置這里不說(shuō)了,自定義約束需要下面3個(gè)屬性
- message 錯(cuò)誤提示信息,可以寫(xiě)死,也可以填寫(xiě)國(guó)際化的key
- groups 分組信息,允許指定此約束所屬的驗(yàn)證組(下面會(huì)說(shuō)到分組約束)
- payload 有效負(fù)載,可以通過(guò)payload來(lái)標(biāo)記一些需要特殊處理的操作
@Repeatable注解和List定義可以讓該注解在同一個(gè)位置重復(fù)多次,通常是不同的配置(比如不同的分組和消息)
@Constraint(validatedBy = {MobileValidator.class})該注解是指明我們的自定義約束的驗(yàn)證器,那下面就看一下驗(yàn)證器的寫(xiě)法,需要實(shí)現(xiàn)javax.validation.ConstraintValidator接口
public class MobileValidator implements ConstraintValidator<Mobile, String> { /** * 手機(jī)驗(yàn)證規(guī)則 */ private Pattern pattern; @Override public void initialize(Mobile mobile) { pattern = Pattern.compile(mobile.regexp()); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; } return pattern.matcher(value).matches(); } }
ConstraintValidator接口定義了在實(shí)現(xiàn)中設(shè)置的兩個(gè)類型參數(shù)。
- 第一個(gè)指定要驗(yàn)證的注解類(如Mobile),
- 第二個(gè)指定驗(yàn)證器可以處理的元素類型(如String);initialize()方法可以訪問(wèn)約束注解的屬性值;isValid()方法用于驗(yàn)證,返回true表示驗(yàn)證通過(guò)
Bean驗(yàn)證規(guī)范建議將空值視為有效。如果null不是元素的有效值,則應(yīng)使用@NotNull 顯式注釋
到這里我們自定義的約束就寫(xiě)好了,可以用個(gè)例子來(lái)測(cè)試一下
public class MobileTest { public void setMobile(@Mobile String mobile){ // to do } private static ExecutableValidator executableValidator; @BeforeAll public static void setUpValidator() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); executableValidator = factory.getValidator().forExecutables(); } @Test public void manufacturerIsNull() throws NoSuchMethodException { MobileTest mobileTest = new MobileTest(); Method method = MobileTest.class.getMethod("setMobile", String.class); Object[] parameterValues = {"1111111"}; Set<ConstraintViolation<MobileTest>> violations = executableValidator.validateParameters( mobileTest, method, parameterValues); violations.forEach(violation -> System.out.println(violation.getMessage())); } }
手機(jī)號(hào)碼不正確
工作原理
@Validated的工作原理
方法級(jí)別參數(shù)校驗(yàn)
在每個(gè)參數(shù)前面聲明約束注解,然后通過(guò)解析參數(shù)注解完成校驗(yàn),這就是方法級(jí)別的參數(shù)校驗(yàn)。 這種方式可以用于任何的Spring Bean的方法上,一般來(lái)說(shuō),這種方式一般會(huì)采用AOP的Around增強(qiáng)完成 在Spring中,是通過(guò)以下步驟完成
- MethodValidationPostProcessor在Bean的初始化完成之后,判斷是否要進(jìn)行AOP代理(類是否被@Validated標(biāo)記)
- MethodValidationInterceptor攔截所有方法,執(zhí)行校驗(yàn)邏輯
- 委派Validator執(zhí)行參數(shù)校驗(yàn)和返回值校驗(yàn),得到ConstraintViolation
- 處理ConstraintViolation
結(jié)論
總之,對(duì)于任何基本驗(yàn)證,我們將在方法調(diào)用中使用 JSR @Valid注釋。另一方面,對(duì)于任何組驗(yàn)證,包括組序列,我們需要 在我們的方法調(diào)用中使用 Spring 的@Validated注釋。所述@Valid 還需要注釋來(lái)觸發(fā)嵌套屬性的驗(yàn)證。
- @Validated的原理本質(zhì)還是AOP。在方法校驗(yàn)上,利用AOP動(dòng)態(tài)攔截方法,利用JSR303 Validator實(shí)現(xiàn)完成校驗(yàn)。在Bean的屬性校驗(yàn)上,則是基于Bean的生命周期,在其初始化前后完成校驗(yàn)
- Spring Validator本質(zhì)實(shí)現(xiàn)還是JSR303 Validaotr,只是能讓其更好的適配Spring Context
- @javax.validation.Valid是JSR303的核心標(biāo)記注解,但是在Spring Framework中被@Validated取代,但是Spring Validator的實(shí)現(xiàn)可以支持兼容@javax.validation.Valid
例如,在MethodValidationPostProcessor提供了setValidatedAnnotationType方法,替換默認(rèn)的@Validated
在Spring MVC中,RequestResponseBodyMethodProcessor對(duì)@RequestBody和@ResponseBody的校驗(yàn)處理,就兼容了@javax.validation.Valid和@Validated
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); binder.validate(validationHints); break; } } } }
參考鏈接:
https://www.baeldung.com/spring-valid-vs-validated
https://docs.oracle.com/javaee/7/api/javax/validation/Valid.html
https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/javax/validation/Validator.html
https://reflectoring.io/bean-validation-with-spring-boot/
https://jcp.org/en/jsr/detail?id=380
https://www.baeldung.com/javax-validation
到此這篇關(guān)于Java中Validated、Valid 、Validator區(qū)別詳解的文章就介紹到這了,更多相關(guān)Validated、Valid 、Validator區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
舉例說(shuō)明JAVA調(diào)用第三方接口的GET/POST/PUT請(qǐng)求方式
在日常工作和學(xué)習(xí)中,有很多地方都需要發(fā)送請(qǐng)求,這篇文章主要給大家介紹了關(guān)于JAVA調(diào)用第三方接口的GET/POST/PUT請(qǐng)求方式的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01SpringBoot集成Hadoop實(shí)現(xiàn)文件的上傳和下載功能
Spring?Hadoop簡(jiǎn)化了Apache?Hadoop,提供了一個(gè)統(tǒng)一的配置模型以及簡(jiǎn)單易用的API來(lái)使用HDFS、MapReduce、Pig以及Hive,這篇文章主要介紹了SpringBoot集成Hadoop實(shí)現(xiàn)文件的上傳和下載,需要的朋友可以參考下2024-07-07mybatis學(xué)習(xí)之路mysql批量新增數(shù)據(jù)的方法
這篇文章主要介紹了mybatis學(xué)習(xí)之路mysql批量新增數(shù)據(jù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02在SpringBoot中使用lombok的注意事項(xiàng)
這篇文章主要介紹了在SpringBoot中使用lombok的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12CommonMark 使用教程:將 Markdown 語(yǔ)法轉(zhuǎn)成 Html
這篇文章主要介紹了CommonMark 使用教程:將 Markdown 語(yǔ)法轉(zhuǎn)成 Html,這個(gè)技巧我們做任何網(wǎng)站都可以用到,而且非常好用。,需要的朋友可以參考下2019-06-06Java map 優(yōu)雅的元素遍歷方式說(shuō)明
這篇文章主要介紹了Java map 優(yōu)雅的元素遍歷方式說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10Java?C++算法題解leetcode801使序列遞增的最小交換次數(shù)
這篇文章主要為大家介紹了Java?C++題解leetcode801使序列遞增的最小交換次數(shù)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10