摘要:就這樣借助相關(guān)約束注解,就非常簡(jiǎn)單明了,語(yǔ)義清晰的優(yōu)雅的完成了方法級(jí)別入?yún)⑿r?yàn)返回值校驗(yàn)的校驗(yàn)。但倘若是返回值校驗(yàn)執(zhí)行了即使是失敗了,方法體也肯定被執(zhí)行了只能哪些類(lèi)型上提出這個(gè)細(xì)節(jié)的目的是約束注解并不是能用在所有類(lèi)型上的。
每篇一句
在《深度工作》中作者提出這么一個(gè)公式:高質(zhì)量產(chǎn)出=時(shí)間*專(zhuān)注度。所以高質(zhì)量的產(chǎn)出不是靠時(shí)間熬出來(lái)的,而是效率為王相關(guān)閱讀
【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例
【小家Java】深入了解數(shù)據(jù)校驗(yàn)(Bean Validation):基礎(chǔ)類(lèi)打點(diǎn)(ValidationProvider、ConstraintDescriptor、ConstraintValidator)
【小家Spring】詳述Spring對(duì)Bean Validation支持的核心API:Validator、SmartValidator、LocalValidatorFactoryBean...
你在書(shū)寫(xiě)業(yè)務(wù)邏輯的時(shí)候,是否會(huì)經(jīng)常書(shū)寫(xiě)大量的判空校驗(yàn)。比如Service層或者Dao層的方法入?yún)?、入?yún)?duì)象、出參中你是否都有自己的一套校驗(yàn)規(guī)則?比如有些字段必傳,有的非必傳;返回值中有些字段必須有值,有的非必須等等~
如上描述的校驗(yàn)邏輯,窺探一下你的代碼,估摸里面有大量的if else吧。此部分邏輯簡(jiǎn)單(因?yàn)楹蜆I(yè)務(wù)關(guān)系不大)卻看起來(lái)眼花繚亂(趕緊偷偷去喵一下你自己的代碼吧,哈哈)。在攻城主鍵變大的時(shí)候,你會(huì)發(fā)現(xiàn)會(huì)有大量的重復(fù)代碼出現(xiàn),這部分就是你入職一個(gè)新公司的吐槽點(diǎn)之一:垃圾代碼。
若你追求干凈的代碼,甚至有代碼潔癖,如上眾多if else的重復(fù)無(wú)意義勞動(dòng)無(wú)疑是你的痛點(diǎn),那么本文應(yīng)該能夠幫到你。
Bean Validation校驗(yàn)其實(shí)是基于DDD思想設(shè)計(jì)的,我們雖然可以不完全的遵從這種思考方式編程,但是其優(yōu)雅的優(yōu)點(diǎn)還是可取的,本文將介紹Spring為此提供的解決方案~
在講解之前,首先就來(lái)體驗(yàn)一把吧~
@Validated(Default.class) public interface HelloService { Object hello(@NotNull @Min(10) Integer id, @NotNull String name); } // 實(shí)現(xiàn)類(lèi)如下 @Slf4j @Service public class HelloServiceImpl implements HelloService { @Override public Object hello(Integer id, String name) { return null; } }
向容器里注冊(cè)一個(gè)處理器:
@Configuration public class RootConfig { @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); } }
測(cè)試:
@Slf4j @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {RootConfig.class}) public class TestSpringBean { @Autowired private HelloService helloService; @Test public void test1() { System.out.println(helloService.getClass()); helloService.hello(1, null); } }
結(jié)果如圖:
完美的校驗(yàn)住了方法入?yún)ⅰ?/p>
注意此處的一個(gè)小細(xì)節(jié):若你自己運(yùn)行這個(gè)案例你得到的參數(shù)名稱(chēng)可能是hello.args0等,而我此處是形參名。是因?yàn)槲沂褂肑ava8的編譯參數(shù):-parameters(此處說(shuō)一點(diǎn):若你的邏輯中強(qiáng)依賴(lài)于此參數(shù),務(wù)必在你的maven中加入編譯插件并且配置好此編譯參數(shù))
若需要校驗(yàn)方法返回值,改寫(xiě)如下:
@NotNull Object hello(Integer id); // 此種寫(xiě)法效果同上 //@NotNull Object hello(Integer id);
運(yùn)行:
javax.validation.ConstraintViolationException: hello.: 不能為null ...
校驗(yàn)完成。就這樣借助Spring+JSR相關(guān)約束注解,就非常簡(jiǎn)單明了,語(yǔ)義清晰的優(yōu)雅的完成了方法級(jí)別(入?yún)⑿r?yàn)、返回值校驗(yàn))的校驗(yàn)。
校驗(yàn)不通過(guò)的錯(cuò)誤信息,再來(lái)個(gè)全局統(tǒng)一的異常處理,就能讓整個(gè)工程都能盡顯完美之勢(shì)。(錯(cuò)誤消息可以從異常ConstraintViolationException的getConstraintViolations()方法里獲得的~)
它是Spring提供的來(lái)實(shí)現(xiàn)基于方法Method的JSR校驗(yàn)的核心處理器~它能讓約束作用在方法入?yún)?、返回?/b>上,如:
public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
官方說(shuō)明:方法里寫(xiě)有JSR校驗(yàn)注解要想其生效的話,要求類(lèi)型級(jí)別上必須使用@Validated標(biāo)注(還能指定驗(yàn)證的Group)
另外提示一點(diǎn):這個(gè)處理器同處理@Async的處理器AsyncAnnotationBeanPostProcessor非常相似,都是繼承自AbstractBeanFactoryAwareAdvisingPostProcessor 的,所以若有興趣再次也推薦@Async的分析博文,可以對(duì)比著觀看和記憶:【小家Spring】Spring異步處理@Async的使用以及原理、源碼分析(@EnableAsync)
// @since 3.1 public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor implements InitializingBean { // 備注:此處你標(biāo)注@Valid是無(wú)用的~~~Spring可不提供識(shí)別 // 當(dāng)然你也可以自定義注解(下面提供了set方法~~~) // 但是注意:若自定義注解的話,此注解只決定了是否要代理,并不能指定分組哦 so,沒(méi)啥事別給自己找麻煩吧 private Class extends Annotation> validatedAnnotationType = Validated.class; // 這個(gè)是javax.validation.Validator @Nullable private Validator validator; // 可以自定義生效的注解 public void setValidatedAnnotationType(Class extends Annotation> validatedAnnotationType) { Assert.notNull(validatedAnnotationType, ""validatedAnnotationType" must not be null"); this.validatedAnnotationType = validatedAnnotationType; } // 這個(gè)方法注意了:你可以自己傳入一個(gè)Validator,并且可以是定制化的LocalValidatorFactoryBean哦~(推薦) public void setValidator(Validator validator) { // 建議傳入LocalValidatorFactoryBean功能強(qiáng)大,從它里面生成一個(gè)驗(yàn)證器出來(lái)靠譜 if (validator instanceof LocalValidatorFactoryBean) { this.validator = ((LocalValidatorFactoryBean) validator).getValidator(); } else if (validator instanceof SpringValidatorAdapter) { this.validator = validator.unwrap(Validator.class); } else { this.validator = validator; } } // 當(dāng)然,你也可以簡(jiǎn)單粗暴的直接提供一個(gè)ValidatorFactory即可~ public void setValidatorFactory(ValidatorFactory validatorFactory) { this.validator = validatorFactory.getValidator(); } // 毫無(wú)疑問(wèn),Pointcut使用AnnotationMatchingPointcut,并且支持內(nèi)部類(lèi)哦~ // 說(shuō)明@Aysnc使用的也是AnnotationMatchingPointcut,只不過(guò)因?yàn)樗С謽?biāo)注在類(lèi)上和方法上,所以最終是組合的ComposablePointcut // 至于Advice通知,此處一樣的是個(gè)`MethodValidationInterceptor`~~~~ @Override public void afterPropertiesSet() { Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true); this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator)); } // 這個(gè)advice就是給@Validation的類(lèi)進(jìn)行增強(qiáng)的~ 說(shuō)明:子類(lèi)可以覆蓋哦~ // @since 4.2 protected Advice createMethodValidationAdvice(@Nullable Validator validator) { return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor()); } }
它是個(gè)普通的BeanPostProcessor,為Bean創(chuàng)建的代理的時(shí)機(jī)是postProcessAfterInitialization(),也就是在Bean完成初始化后有必要的話用一個(gè)代理對(duì)象返回進(jìn)而交給Spring容器管理~(同@Aysnc)
容易想到,關(guān)于校驗(yàn)方面的邏輯不在于它,而在于切面的通知:MethodValidationInterceptor
它是AOP聯(lián)盟類(lèi)型的通知,此處專(zhuān)門(mén)用于處理方法級(jí)別的數(shù)據(jù)校驗(yàn)。
注意理解方法級(jí)別:方法級(jí)別的入?yún)⒂锌赡苁歉鞣N平鋪的參數(shù)、也可能是一個(gè)或者多個(gè)對(duì)象
// @since 3.1 因?yàn)樗r?yàn)Method 所以它使用的是javax.validation.executable.ExecutableValidator public class MethodValidationInterceptor implements MethodInterceptor { // javax.validation.Validator private final Validator validator; // 如果沒(méi)有指定校驗(yàn)器,那使用的就是默認(rèn)的校驗(yàn)器 public MethodValidationInterceptor() { this(Validation.buildDefaultValidatorFactory()); } public MethodValidationInterceptor(ValidatorFactory validatorFactory) { this(validatorFactory.getValidator()); } public MethodValidationInterceptor(Validator validator) { this.validator = validator; } @Override @SuppressWarnings("unchecked") public Object invoke(MethodInvocation invocation) throws Throwable { // Avoid Validator invocation on FactoryBean.getObjectType/isSingleton // 如果是FactoryBean.getObject() 方法 就不要去校驗(yàn)了~ if (isFactoryBeanMetadataMethod(invocation.getMethod())) { return invocation.proceed(); } Class>[] groups = determineValidationGroups(invocation); // Standard Bean Validation 1.1 API ExecutableValidator是1.1提供的 ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set> result; // 錯(cuò)誤消息result 若存在最終都會(huì)ConstraintViolationException異常形式拋出 try { // 先校驗(yàn)方法入?yún)? result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // 此處回退了異步:找到bridged method方法再來(lái)一次 methodToValidate = BridgeMethodResolver.findBridgedMethod(ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass())); result = execVal.validateParameters(invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } if (!result.isEmpty()) { // 有錯(cuò)誤就拋異常拋出去 throw new ConstraintViolationException(result); } // 執(zhí)行目標(biāo)方法 拿到返回值后 再去校驗(yàn)這個(gè)返回值 Object returnValue = invocation.proceed(); result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } return returnValue; } // 找到這個(gè)方法上面是否有標(biāo)注@Validated注解 從里面拿到分組信息 // 備注:雖然代理只能標(biāo)注在類(lèi)上,但是分組可以標(biāo)注在類(lèi)上和方法上哦~~~~ protected Class>[] determineValidationGroups(MethodInvocation invocation) { Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class); if (validatedAnn == null) { validatedAnn = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class); } return (validatedAnn != null ? validatedAnn.value() : new Class>[0]); } }
這個(gè)Advice的實(shí)現(xiàn),簡(jiǎn)單到不能再簡(jiǎn)單了,稍微有點(diǎn)基礎(chǔ)的應(yīng)該都能很容易看懂吧(據(jù)我不完全估計(jì)這個(gè)應(yīng)該是最簡(jiǎn)單的)。
==使用細(xì)節(jié)==(重要)文首雖然已經(jīng)給了一個(gè)使用示例,但是那畢竟只是局部。在實(shí)際生產(chǎn)使用中,比如上面理論更重要的是一些使用細(xì)節(jié)(細(xì)節(jié)往往是區(qū)分你是不是高手的地方),這里從我使用的經(jīng)驗(yàn)中,總結(jié)如下幾點(diǎn)供給大家參考(基本算是分享我躺過(guò)的坑):
使用@Validated去校驗(yàn)方法Method,不管從使用上還是原理上,都是非常簡(jiǎn)單和簡(jiǎn)約的,建議大家在企業(yè)應(yīng)用中多多使用。1、約束注解(如@NotNull)不能放在實(shí)體類(lèi)上
一般情況下,我們對(duì)于Service層驗(yàn)證(Controller層一般都不給接口),大都是面向接口編程和使用,那么這種@NotNull放置的位置應(yīng)該怎么放置呢?
看這個(gè)例子:
public interface HelloService { Object hello(@NotNull @Min(10) Integer id, @NotNull String name); } @Validated(Default.class) @Slf4j @Service public class HelloServiceImpl implements HelloService { @Override public Object hello(Integer id, String name) { return null; } }
約束條件都寫(xiě)在實(shí)現(xiàn)類(lèi)上,按照我們所謂的經(jīng)驗(yàn),應(yīng)該是不成問(wèn)題的。但運(yùn)行:
javax.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not redefine the parameter constraint configuration, but method HelloServiceImpl#hello(Integer) redefines the configuration of HelloService#hello(Integer). at org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints.apply(OverridingMethodMustNotAlterParameterConstraints.java:24) ...
重說(shuō)三:請(qǐng)務(wù)必注意請(qǐng)務(wù)必注意請(qǐng)務(wù)必注意這個(gè)異常是javax.validation.ConstraintDeclarationException,而不是錯(cuò)誤校驗(yàn)錯(cuò)誤異常javax.validation.ConstraintViolationException。請(qǐng)?jiān)谧鋈之惓2东@的時(shí)候一定要區(qū)分開(kāi)來(lái)~
異常信息是說(shuō)parameter constraint configuration在校驗(yàn)方法入?yún)?/strong>的約束時(shí),若是@Override父類(lèi)/接口的方法,那么這個(gè)入?yún)⒓s束只能寫(xiě)在父類(lèi)/接口上面~~~
至于為什么只能寫(xiě)在接口處,這個(gè)具體原因其實(shí)是和Bean Validation的實(shí)現(xiàn)產(chǎn)品有關(guān)的,比如使用的Hibernate校驗(yàn),原因可參考它的此類(lèi):OverridingMethodMustNotAlterParameterConstraints
還需注意一點(diǎn):若實(shí)現(xiàn)類(lèi)寫(xiě)的約束和接口一模一樣,那也是沒(méi)問(wèn)題的。比如上面若實(shí)現(xiàn)類(lèi)這么寫(xiě)是沒(méi)有問(wèn)題能夠完成正常校驗(yàn)的:
@Override public Object hello(@NotNull @Min(10) Integer id, @NotNull String name) { return null; }
雖然能正常work完成校驗(yàn),但需要深刻理解一模一樣這四個(gè)字。簡(jiǎn)單的說(shuō)把10改成9都會(huì)報(bào)ConstraintDeclarationException異常,更別談移除某個(gè)注解了(不管多少字段多少注解,但凡只要寫(xiě)了一個(gè)就必須保證一模一樣)。
關(guān)于@Override方法校驗(yàn)返回值方面:即使寫(xiě)在實(shí)現(xiàn)類(lèi)里也不會(huì)拋ConstraintDeclarationException
另外@Validated注解它寫(xiě)在實(shí)現(xiàn)類(lèi)/接口上均可~
最后你應(yīng)該自己領(lǐng)悟到:若入?yún)⑿r?yàn)失敗了,方法體是不會(huì)執(zhí)行的。但倘若是返回值校驗(yàn)執(zhí)行了(即使是失敗了),方法體也肯定被執(zhí)行了~~2、@NotEmpty/@NotBlank只能哪些類(lèi)型上?
提出這個(gè)細(xì)節(jié)的目的是:約束注解并不是能用在所有類(lèi)型上的。比如若你把@NotEmpty讓它去驗(yàn)證Object類(lèi)型,它會(huì)報(bào)錯(cuò)如下:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint "javax.validation.constraints.NotEmpty" validating type "java.lang.Object". Check configuration for "hello."
需要強(qiáng)調(diào)的是:若標(biāo)注在方法上是驗(yàn)證返回值的,這個(gè)時(shí)候方法體是已經(jīng)執(zhí)行了的,這個(gè)和ConstraintDeclarationException不一樣~
對(duì)這兩個(gè)注解依照官方文檔做如下簡(jiǎn)要說(shuō)明。@NotEmpty只能標(biāo)注在如下類(lèi)型
CharSequence
Collection
Map
Array
注意:""它是空的,但是" "就不是了
@NotBlank只能使用在CharSequence上,它是Bean Validation 2.0新增的注解~
3、接口和實(shí)現(xiàn)類(lèi)上都有注解,以誰(shuí)為準(zhǔn)?這個(gè)問(wèn)題有個(gè)隱含條件:只有校驗(yàn)方法返回值時(shí)才有這種可能性。
public interface HelloService { @NotEmpty String hello(@NotNull @Min(10) Integer id, @NotNull String name); } @Slf4j @Service @Validated(Default.class) public class HelloServiceImpl implements HelloService { @Override public @NotNull String hello(Integer id, String name) { return ""; } }
運(yùn)行案例,helloService.hello(18, "fsx");打印如下:
javax.validation.ConstraintViolationException: hello.: 不能為空 ...
到這里,可能有小伙伴就會(huì)早早下結(jié)論:當(dāng)同時(shí)存在時(shí),以接口的約束為準(zhǔn)。
那么,我只把返回值稍稍修改,你再看一下呢???
@Override public @NotNull String hello(Integer id, String name) { return null; // 返回值改為null }
再運(yùn)行:
javax.validation.ConstraintViolationException: hello.: 不能為空, hello. : 不能為null ...
透過(guò)打印的信息,結(jié)論就自然不必我多。但是有個(gè)道理此處可說(shuō)明:大膽猜測(cè),小心求證
4、如何校驗(yàn)級(jí)聯(lián)屬性?在實(shí)際開(kāi)發(fā)中,其實(shí)大多數(shù)情況下我們方法入?yún)⑹莻€(gè)對(duì)象(甚至對(duì)象里面有對(duì)象),而不是單單平鋪的參數(shù),因此就介紹一個(gè)級(jí)聯(lián)屬性校驗(yàn)的例子:
@Getter @Setter @ToString public class Person { @NotNull private String name; @NotNull @Positive private Integer age; @Valid // 讓InnerChild的屬性也參與校驗(yàn) @NotNull private InnerChild child; @Getter @Setter @ToString public static class InnerChild { @NotNull private String name; @NotNull @Positive private Integer age; } } public interface HelloService { String cascade(@NotNull @Valid Person father, @NotNull Person mother); } @Slf4j @Service @Validated(Default.class) public class HelloServiceImpl implements HelloService { @Override public String cascade(Person father, Person mother) { return "hello cascade..."; } }
運(yùn)行測(cè)試用例:
@Test public void test1() { helloService.cascade(null, null); }
輸出如下:
cascade.father: 不能為null, cascade.mother: 不能為null
此處說(shuō)明一點(diǎn):若你father前面沒(méi)加@NotNull,那打印的消息只有:cascade.mother: 不能為null
我把測(cè)試用例改造如下,你繼續(xù)感受一把:
@Test public void test1() { Person father = new Person(); father.setName("fsx"); Person.InnerChild innerChild = new Person.InnerChild(); innerChild.setAge(-1); father.setChild(innerChild); helloService.cascade(father, new Person()); }
錯(cuò)誤消息如下(請(qǐng)小伙伴仔細(xì)觀察和分析緣由):
cascade.father.age: 不能為null, cascade.father.child.name: 不能為null, cascade.father.child.age: 必須是正數(shù)
思考:為何mother的相關(guān)屬性以及子屬性為何全都沒(méi)有校驗(yàn)?zāi)兀?/p> 5、循環(huán)依賴(lài)問(wèn)題
上面說(shuō)了Spring對(duì)@Validated的處理和對(duì)@Aysnc的代理邏輯是差不多的,有了之前的經(jīng)驗(yàn),很容易想到它也存在著如題的問(wèn)題:比如HelloService的A方法想調(diào)用本類(lèi)的B方法,但是很顯然我是希望B方法的方法校驗(yàn)是能生效的,因此其中一個(gè)做法就是注入自己,使用自己的代理對(duì)象來(lái)調(diào)用:
public interface HelloService { Object hello(@NotNull @Min(10) Integer id, @NotNull String name); String cascade(@NotNull @Valid Person father, @NotNull Person mother); } @Slf4j @Service @Validated(Default.class) public class HelloServiceImpl implements HelloService { @Autowired private HelloService helloService; @Override public Object hello(@NotNull @Min(10) Integer id, @NotNull String name) { helloService.cascade(null, null); // 調(diào)用本類(lèi)方法 return null; } @Override public String cascade(Person father, Person mother) { return "hello cascade..."; } }
運(yùn)行測(cè)試用例:
@Test public void test1() { helloService.hello(18, "fsx"); // 入口方法校驗(yàn)通過(guò),內(nèi)部調(diào)用cascade方法希望繼續(xù)得到校驗(yàn) }
運(yùn)行報(bào)錯(cuò):
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name "helloServiceImpl": Bean with name "helloServiceImpl" has been injected into other beans [helloServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using "getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean ...
這個(gè)報(bào)錯(cuò)消息不可為不熟悉。關(guān)于此現(xiàn)象,之前做過(guò)非常非常詳細(xì)的說(shuō)明并且提供了多種解決方案,所以此處略過(guò)。
若關(guān)于此問(wèn)的原因和解決方案不明白的,請(qǐng)移步此處:【小家Spring】使用@Async異步注解導(dǎo)致該Bean在循環(huán)依賴(lài)時(shí)啟動(dòng)報(bào)BeanCurrentlyInCreationException異常的根本原因分析,以及提供解決方案
雖然我此處不說(shuō)解決方案,但我提供問(wèn)題解決后運(yùn)行的打印輸出情況,供給小伙伴調(diào)試參考,此舉很暖心有木有:
javax.validation.ConstraintViolationException: cascade.mother: 不能為null, cascade.father: 不能為null ...總結(jié)
本文介紹了Spring提供給我們方法級(jí)別校驗(yàn)的能力,在企業(yè)應(yīng)用中使用此種方式完成絕大部分的基本校驗(yàn)工作,能夠讓我們的代碼更加簡(jiǎn)潔、可控并且可擴(kuò)展,因此我是推薦使用和擴(kuò)散的~
在文末有必要強(qiáng)調(diào)一點(diǎn):關(guān)于上面級(jí)聯(lián)屬性的校驗(yàn)時(shí)使用的@Valid注解你使用@Validated可替代不了,不會(huì)有效果的。
至于有小伙伴私信我疑問(wèn)的問(wèn)題:為何他Controller方法中使用@Valid和@Validated均可,并且網(wǎng)上同意給的答案都是都可用,差不多???還是那句話:這是下篇文章的重點(diǎn),請(qǐng)持續(xù)關(guān)注~
稍稍說(shuō)一下它的弊端:因?yàn)樾r?yàn)失敗它最終采用的是拋異常方式來(lái)中斷,因此效率上有那么一丟丟的損耗。but,你的應(yīng)用真的需要考慮這種極致性能問(wèn)題嗎?這才是你該思考的~知識(shí)交流
若文章格式混亂,可點(diǎn)擊:原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接
==The last:如果覺(jué)得本文對(duì)你有幫助,不妨點(diǎn)個(gè)贊唄。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==
**若對(duì)技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群。
若群二維碼失效,請(qǐng)加wx號(hào):fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會(huì)手動(dòng)邀請(qǐng)入群**
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75584.html
摘要:畢竟永遠(yuǎn)相信本文能給你帶來(lái)意想不到的收獲使用示例關(guān)于數(shù)據(jù)校驗(yàn)這一塊在中的使用案例,我相信但凡有點(diǎn)經(jīng)驗(yàn)的程序員應(yīng)該沒(méi)有不會(huì)使用的,并且還不乏熟練的選手。 每篇一句 NBA里有兩大笑話:一是科比沒(méi)天賦,二是詹姆斯沒(méi)技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validati...
摘要:方案一借助對(duì)方法級(jí)別數(shù)據(jù)校驗(yàn)的能力首先必須明確一點(diǎn)此能力屬于框架的,而部分框架。 每篇一句 在金字塔塔尖的是實(shí)踐,學(xué)而不思則罔,思而不學(xué)則殆(現(xiàn)在很多編程框架都只是教你碎片化的實(shí)踐) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例【小家Spr...
摘要:本文主要介紹在中自動(dòng)校驗(yàn)的機(jī)制。引入依賴(lài)我們使用構(gòu)建應(yīng)用來(lái)進(jìn)行演示。在中校驗(yàn)數(shù)據(jù)值得注意的地方參數(shù)前需要加上注解,表明需要對(duì)其進(jìn)行校驗(yàn),而校驗(yàn)的信息會(huì)存放到其后的中。層改寫(xiě)方法限定需要進(jìn)行校驗(yàn),而方法則不做限制。 簡(jiǎn)介 JSR303/JSR-349,hibernate validation,spring validation之間的關(guān)系。JSR303是一項(xiàng)標(biāo)準(zhǔn),JSR-349是其的升級(jí)版...
摘要:初步使用主要使用注解的方式對(duì)進(jìn)行校驗(yàn),第一個(gè)例子在需要校驗(yàn)的字段上指定約束條件然后在中可以這樣調(diào)用,加上注解即可。如果校驗(yàn)失敗,默認(rèn)會(huì)返回框架的出錯(cuò)信息。指定到的分組名會(huì)全部進(jìn)行校驗(yàn),不指定的不校驗(yàn)。 Spring Boot - 表單校驗(yàn)(JSR303&Hibernate Validator) 回顧 Spring Boot - 初識(shí) Hello World Spring Boot -...
摘要:如果說(shuō)要使用數(shù)據(jù)校驗(yàn),我十分相信小伙伴們都能夠使用,但估計(jì)大都是有個(gè)前提的環(huán)境。具體使用可參考小家讓支持對(duì)平鋪參數(shù)執(zhí)行數(shù)據(jù)校驗(yàn)?zāi)J(rèn)使用只能對(duì)進(jìn)行校驗(yàn)級(jí)聯(lián)校驗(yàn)什么叫級(jí)聯(lián)校驗(yàn),其實(shí)就是帶校驗(yàn)的成員里存在級(jí)聯(lián)對(duì)象時(shí),也要對(duì)它完成校驗(yàn)。 每篇一句 NBA里有兩大笑話:一是科比沒(méi)天賦,二是詹姆斯沒(méi)技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(...
閱讀 1850·2021-11-11 16:54
閱讀 2073·2019-08-30 15:56
閱讀 2385·2019-08-30 15:44
閱讀 1322·2019-08-30 15:43
閱讀 1878·2019-08-30 11:07
閱讀 833·2019-08-29 17:11
閱讀 1485·2019-08-29 15:23
閱讀 3021·2019-08-29 13:01