摘要:可能有人認為數(shù)據(jù)校驗?zāi)K并不是那么的重要,因為硬編碼都可以做。我以數(shù)據(jù)綁定為引子引出了數(shù)據(jù)校驗這一塊,是想表明它的重要性。關(guān)于數(shù)據(jù)校驗這塊,最新的是,也就是我們常說的。
每篇一句
吾皇一日不退役,爾等都是臣子
前幾篇文章在講Spring的數(shù)據(jù)綁定的時候,多次提到過數(shù)據(jù)校驗??赡苡腥苏J為數(shù)據(jù)校驗?zāi)K并不是那么的重要,因為硬編碼都可以做。若是這么想的話,那就大錯特錯了~
前面講解DataBinder的時候一個小細節(jié),它所在的包是:org.springframework.validation,并且在分析源碼的時候能看到DataBinder它不僅能夠完成數(shù)據(jù)綁定,也提供了對數(shù)據(jù)校驗的支持且還保存了校驗結(jié)果。
我以數(shù)據(jù)綁定DataBinder為引子引出了數(shù)據(jù)校驗這一塊,是想表明它的重要性。連Java都把它抽象成了JSR標準進行提出,so我認為這塊是必修課,有必要了解本章的內(nèi)容。
為什么要有數(shù)據(jù)校驗?數(shù)據(jù)校驗 是非常常見的工作,在日常的開發(fā)中貫穿于代碼的各個層次,從上層的View層到底層的數(shù)據(jù)層。
在此處有必要再強調(diào)一句:前面說了數(shù)據(jù)綁定并不屬于Spring MVC的專利,同樣的數(shù)據(jù)校驗也不是只會發(fā)生在web層,它可以在任意一層,從后面的示例中你會有更深的理解
在任何時候,當你要處理一個應(yīng)用程序的業(yè)務(wù)邏輯,數(shù)據(jù)校驗是你必須要考慮和面對的事情。應(yīng)用程序必須通過某種手段來確保輸入進來的數(shù)據(jù)從語義上來講是正確的(比如生日必須是過去時,年齡必須>0等等~)。
我們知道通常情況下程序肯定是分層的,不同的層一般由不同的人來開發(fā)。若你是一個有經(jīng)驗的程序員, 我相信你肯定見過在不同的層了都出現(xiàn)了相同的校驗代碼,這就是某種意義上的垃圾代碼。
public String queryValueByKey(String parmTemplateCode, String conditionName, String conditionKey, String resultName) { checkNotNull(parmTemplateCode, "parmTemplateCode not null"); checkNotNull(conditionName, "conditionName not null"); checkNotNull(conditionKey, "conditionKey not null"); checkNotNull(resultName, "resultName not null"); ... }
從這個簡單的方法入?yún)⑿r炛辽倌馨l(fā)現(xiàn)如下問題:
需要寫大量的代碼來進行參數(shù)驗證。(這種代碼多了就算垃圾代碼)
需要通過注釋來知道每個入?yún)⒌募s束是什么(否則別人咋看得懂)
每個程序員做參數(shù)驗證的方式不一樣,參數(shù)驗證不通過拋出的異常也不一樣(后期幾乎沒法維護)
如上會導(dǎo)致代碼冗余和一些管理的問題(代碼量越大,管理起來維護起來就越困難),比如說語義的一致性等。為了避免這樣的情況發(fā)生,最好是將驗證邏輯與相應(yīng)的域模型(領(lǐng)域模型的概念)進行綁定,這就是本文提供的一個新思路(其實是JavaEE提供的思路)
為了解決這個問題,Bean Validation 為 JavaBean 驗證定義了相應(yīng)的元數(shù)據(jù)模型和 API。默認的元數(shù)據(jù)是 各種Java Annotations,當然也支持xml方式并且你也可以擴展~
可以說Bean Validation是JavaBean的一個拓展,它可以布局于任意一層代碼,不局限于Web應(yīng)用還是端應(yīng)用。
JSR是Java Specification Requests的縮寫,意思是Java 規(guī)范提案。關(guān)于數(shù)據(jù)校驗這塊,最新的是JSR380,也就是我們常說的Bean Validation 2.0。
Bean Validation 2.0 是JSR第380號標準。該標準連接如下:https://www.jcp.org/en/egc/vi...
Bean Validation的主頁:http://beanvalidation.org
Bean Validation的參考實現(xiàn):https://github.com/hibernate/...
Bean Validation是一個通過配置注解來驗證參數(shù)的框架,它包含兩部分Bean Validation API(規(guī)范)和Hibernate Validator(實現(xiàn))。
Bean Validation是Java定義的一套基于注解/xml的數(shù)據(jù)校驗規(guī)范,目前已經(jīng)從JSR 303的1.0版本升級到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已經(jīng)經(jīng)歷了三個版本(我截圖如下:)
現(xiàn)在絕大多數(shù)coder使用者其實都還在使用Bean Validation 1.1,畢竟一般來說它已經(jīng)夠用了~
本文會介紹Bean Validation 2.0提供的一些實用的新東西,畢竟Java8現(xiàn)在已成為主流,完全可以使用了~
要想使用它,首先就得導(dǎo)包嘛~根據(jù)經(jīng)驗,和JCache類似Java只提供了規(guī)范,并沒有提供實現(xiàn),所以我們可以先找到它的API包然后導(dǎo)入:
javax.validation validation-api 2.0.1.Final
關(guān)于版本之間的差異其實不是本文說明的重點,畢竟2.0做到了很好的向下兼容,使用起來是無縫的。
但是本處還是給個1.1版本和2.0.1的截圖,感官上簡單對比一下區(qū)別:
Bean Validation | Hibernate Validation | JDK | Spring Boot |
---|---|---|---|
1.1 | 5.4 + | 6+ | 1.5.x |
2.0 | 6.0 + | 8+ | 2.0.x |
因為2.0推出的時間確實不算長,so此處我把一些重要的關(guān)注點列舉如下:
對Java的最低版本要求是Java 8
支持容器的校驗,通過TYPE_USE類型的注解實現(xiàn)對容器內(nèi)容的約束:List<@Email String>
支持日期/時間的校驗,@Past和@Future
拓展元數(shù)據(jù)(新增注解):@Email,@NotEmpty,@NotBlank,@Positive, @PositiveOrZero,@Negative,@NegativeOrZero,@PastOrPresent和@FutureOrPresent
1. 像`@Email、@NotEmpty、@NotBlank`之前是Hibernate額外提供的,2.0標準后hibernate自動退位讓賢并且標注為過期了
Bean Validation 2.0的唯一實現(xiàn)為Hibernate Validator。(其實還有Apache BVal,但是你懂的,forget it)
對于Hibernate Validator,它自己也擴展了一些注解支持。
1. **6.0以上**版本新增(對應(yīng)標準2.0版本):`@UniqueElements、@ISBN、@CodePointLength` 2. **6.0以下**版本可以使用的: `@URL、@ScriptAssert、@SafeHtml、@Range、@ParameterScriptAssert、@Mod11Check、@Mod10Check、@LuhnCheck、@Length、@EAN、@Currency、@CreditCardNumber、@ConstraintComposition、` 3. `Hibernate Validator`默認會校驗完所有的屬性,然后**返回所有的**驗證`失敗信息`。開啟fail fast mode后,只要有一個驗證失敗,則返回驗證失敗信息。
so,對于Java Bean Validation的實現(xiàn)落地產(chǎn)品就沒啥好選的,導(dǎo)入Hibernate Validator(最新版本)吧:
org.hibernate.validator hibernate-validator 6.0.17.Final
==小細節(jié):==
可以看到,導(dǎo)入了hibernate-validator就必要再自己導(dǎo)入Java Bean ValidationAPI了,因此建議不用再手動導(dǎo)入API,交給內(nèi)部來管理依賴。
定義一個待校驗的普通JavaBean:
@Getter @Setter @ToString public class Person { // 錯誤消息message是可以自定義的 @NotNull(message = "名字不能為null") public String name; @Positive public Integer age; @NotNull @NotEmpty private List<@Email String> emails; @Future private Date start; }
書寫測試用例:
public static void main(String[] args) { Person person = new Person(); //person.setName("fsx"); person.setAge(-1); // email校驗:雖然是List都可以校驗哦 person.setEmails(Arrays.asList("[email protected]", "[email protected]", "aaa.com")); //person.setStart(new Date()); //start 需要是一個將來的時間: Sun Jul 21 10:45:03 CST 2019 //person.setStart(new Date(System.currentTimeMillis() + 10000)); //校驗通過 // 對person進行校驗然后拿到結(jié)果(顯然使用時默認的校驗器) 會保留下校驗失敗的消息 Set> result = Validation.buildDefaultValidatorFactory().getValidator().validate(person); // 對結(jié)果進行遍歷輸出 result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()) .forEach(System.out::println); }
運行,報錯啦:
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ...
可以看到運行必須依賴于javax.el這個包。(其實我是比較費解的,為何校驗框架非得依賴它呢?有小伙伴可以幫忙解釋一下嗎?)
那行,導(dǎo)入依賴javax.el以及它的實現(xiàn):
javax.el javax.el-api 3.0.1-b06 org.glassfish.web javax.el 2.2.6
需要注意的是,網(wǎng)上大都建議導(dǎo)入org.glassfish.web包。但是EL3.0后它并沒有再提供支持了,因此我個人是不建議使用它,而是使用下面tomcat的實現(xiàn)的~
關(guān)于EL的實現(xiàn)此處啰嗦一句:JavaEE并沒有提供el的實現(xiàn),需要容器自行提供,比如上面你想要導(dǎo)入最為流行的tomcat,你可以導(dǎo)入如下jar即可:
org.apache.tomcat.embed tomcat-embed-el 9.0.22 org.apache.tomcat tomcat-jasper-el 9.0.22 provided
此處還需要說明一點的是:嵌入式tomcat(比如SpringBoot環(huán)境)若要使用時需要顯示導(dǎo)入的。但是傳統(tǒng)tomcat中你若要使用是不用自己導(dǎo)入的(tomcat自帶此jar)。
但是,但是,但是自從tomcat8.5后不再自帶jsper-el的包了,需要手動導(dǎo)入。(tomcat7還是有的~)
==最佳實踐:==
一般來說,javax.el-api以及validation-api都是沒有必要多帶帶導(dǎo)入的,第三方包都會自帶。所以絕大數(shù)情況下,我們只需要這么導(dǎo)入即可正常work,形如下面這樣非常趕緊整潔:
org.hibernate.validator hibernate-validator 6.0.17.Final org.apache.tomcat.embed tomcat-embed-el 9.0.22
答:那是因為絕大多數(shù)情況下你使用@Valid是使用在Spring MVC上,它是不依賴于EL方式的,下篇文章會詳細說明關(guān)于數(shù)據(jù)校驗在Spring上的使用。而本文主要還是講解API的方式~
經(jīng)過一番導(dǎo)包后,再次運行打印如下(方式一、方式二結(jié)果一致):
name名字不能為null: null // 此處錯誤消息是自己的自定義內(nèi)容 age必須是正數(shù): -1 emails[2].不是一個合法的電子郵件地址: aaa.com
這樣通過API調(diào)用的方式就完成了對這個JavaBean的屬性校驗~
核心API分析 Validation官方給它的定義為:This class is the entry point for Bean Validation.它作為校驗的入口,有三種方式來啟動它:
最簡單方式:使用默認的ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 雖然是默認的單也會有如下2種情況:
1. 若使用了xml配置了一個provider,那就會使用這個provider來提供Factory 2. 若沒有xml或者xml力沒有配置provider,那就是用默認的`ValidationProviderResolver`實現(xiàn)類來處理
方式二:選擇自定義的ValidationProviderResolver來跟XML配置邏輯選出一個ValidationProvider來。大致代碼如下:
Configuration configuration = Validation.byDefaultProvider() .providerResolver(new MyResolverStrategy()) // 自定義一個ValidationProviderResolver的實現(xiàn)類 .configure(); ValidatorFactory factory = configuration.buildValidatorFactory();
第三種方式就更加自由了:你可以直接提供一個類型安全的ValidationProvider實現(xiàn)。比如HibernateValidator就是一個ValidationProvider的實現(xiàn):
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class) // .providerResolver( ... ) // 因為制定了Provider,這個參數(shù)就可選了 .configure() .failFast(false); ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
這三種初始化方式,在源碼處就是對應(yīng)提供的三個public static方法:
public class Validation { // 方式一 public static ValidatorFactory buildDefaultValidatorFactory() { return byDefaultProvider().configure().buildValidatorFactory(); } // 方式二 public static GenericBootstrap byDefaultProvider() { return new GenericBootstrapImpl(); } // 方式三 public static, U extends ValidationProvider > ProviderSpecificBootstrap byProvider(Class providerType) { return new ProviderSpecificBootstrapImpl<>( providerType ); } ... }
對于若你想使用xml文件獨立配置校驗規(guī)則,可以使用Configuration.addMapping(new FileInputStream(validationFile));,現(xiàn)在很少這么使用,略~
使用注意事項:ValidatorFactory被創(chuàng)建后應(yīng)該緩存起來再提供使用,因為它是縣城安全的。
因為現(xiàn)在都會使用Hibernate-Validation來處理校驗,因此此處只關(guān)心方式三~
HibernateValidatorConfiguration此接口表示配置,繼承自標注接口javax.validation.Configuration。很明顯,它是HibernateValidator的專屬配置類
先看頂級接口:javax.validation.Configuration,為構(gòu)建ValidatorFactory的配置類。默認情況下,它會讀取配置文件META-INF/validation.xml,Configuration提供的API方法是覆蓋xml配置文件項的。若沒有找到validation.xml,就會使用默認的ValidationProviderResolver也就是:DefaultValidationProviderResolver。
public interface Configuration> { // 該方法調(diào)用后就不會再去找META-INF/validation.xml了 T ignoreXmlConfiguration(); // 消息內(nèi)插器 它是個狠角色,關(guān)于它的使用場景,后續(xù)會有詳解(包括Spring都實現(xiàn)了它來做事) // 它的作用是:插入給定的約束沖突消息 T messageInterpolator(MessageInterpolator interpolator); // 確定bean驗證提供程序是否可以訪問屬性的協(xié)定。對每個正在驗證或級聯(lián)的屬性調(diào)用此約定。(Spring木有實現(xiàn)它) // 對每個正在驗證或級聯(lián)的屬性都會調(diào)用此約定 // Traversable: 可移動的 T traversableResolver(TraversableResolver resolver); // 創(chuàng)建ConstraintValidator的工廠 // ConstraintValidator:定義邏輯以驗證給定對象類型T的給定約束A。(A是個注解類型) T constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory); // ParameterNameProvider:提供Constructor/Method的方法名們 T parameterNameProvider(ParameterNameProvider parameterNameProvider); // java.time.Clock 用作判定@Future和@Past(默認取值當前時間) // 若你希望他是個邏輯實現(xiàn),提供一個它即可 // @since 2.0 T clockProvider(ClockProvider clockProvider); // 值提取器。這是add哦~ 負責從Optional、List等這種容器里提取值~ // @since 2.0 T addValueExtractor(ValueExtractor> extractor); // 加載xml文件 T addMapping(InputStream stream); // 添加特定的屬性給Provider用的。此屬性等效于XML配置屬性。 // 此方法通常是框架自己分析xml文件得到屬性值然后放進去,調(diào)用者一般不使用(當然也可以用) T addProperty(String name, String value); // 下面都是get方法嘍 MessageInterpolator getDefaultMessageInterpolator(); TraversableResolver getDefaultTraversableResolver(); ConstraintValidatorFactory getDefaultConstraintValidatorFactory(); ParameterNameProvider getDefaultParameterNameProvider(); ClockProvider getDefaultClockProvider(); BootstrapConfiguration getBootstrapConfiguration(); // 整個配置也可返回出去 // 上面都是工作,這個方法才是最終需要調(diào)用的:得到一個ValidatorFactory ValidatorFactory buildValidatorFactory(); }
該接口提供了一些標準的配置項。在實際應(yīng)用中都是使用Hibernate Validation,所以再看看這個具體的子接口:
public interface HibernateValidatorConfiguration extends Configuration{ // 這批屬性,證明直接可以通過System屬性值來控制,大大地方便~ // 這個機制快速失敗機制:true檢查完一個有錯誤就返回,false全部檢查完把錯誤消息一起返回 默認false String FAIL_FAST = "hibernate.validator.fail_fast"; String ALLOW_PARAMETER_CONSTRAINT_OVERRIDE = "hibernate.validator.allow_parameter_constraint_override"; String ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT = "hibernate.validator.allow_multiple_cascaded_validation_on_result"; String ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS = "hibernate.validator.allow_parallel_method_parameter_constraint"; // @since 5.2 @Deprecated String CONSTRAINT_MAPPING_CONTRIBUTOR = "hibernate.validator.constraint_mapping_contributor"; // @since 5.3 String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors"; // @since 6.0.3 String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache"; // @since 6.0.3 ScriptEvaluatorFactory:執(zhí)行腳本 @Incubating String SCRIPT_EVALUATOR_FACTORY_CLASSNAME = "hibernate.validator.script_evaluator_factory"; // @since 6.0.5 comparing date/time in temporal constraints. In milliseconds. @Incubating String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance"; // ResourceBundleMessageInterpolator用于 load resource bundles ResourceBundleLocator getDefaultResourceBundleLocator(); // 創(chuàng)建一個ConstraintMapping:通過編程API配置的約束映射 // 設(shè)置映射后,必須通過addMapping(constraintmapping)將其添加到此配置中。 ConstraintMapping createConstraintMapping(); // 拿到所有的值提取器 @since 6.0 @Incubating Set > getDefaultValueExtractors(); // 往下就開始配置了~~~~~~~~~~ HibernateValidatorConfiguration addMapping(ConstraintMapping mapping); HibernateValidatorConfiguration failFast(boolean failFast); // used for loading user-provided resources: HibernateValidatorConfiguration externalClassLoader(ClassLoader externalClassLoader); // true:表示允許覆蓋約束的方法。false表示不予許(拋出異常) 默認值是false HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow); // 定義是否允許對返回值標記多個約束以進行級聯(lián)驗證。 默認是false HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow); // 定義約束的**并行方法**是否應(yīng)引發(fā)ConstraintDefinitionException HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow); // 是否允許緩存TraversableResolver 默認值是true HibernateValidatorConfiguration enableTraversableResolverResultCache(boolean enabled); // 設(shè)置一個腳本執(zhí)行器 @Incubating HibernateValidatorConfiguration scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory); // 允許在時間約束中比較日期/時間時設(shè)置可接受的誤差范圍 // 比如@Past @PastOrPresent @Future @FutureOrPresent @Incubating HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance); // 允許設(shè)置將傳遞給約束驗證器的有效負載。如果多次調(diào)用該方法,則只傳播最后傳遞的有效負載。 @Incubating HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload); }
關(guān)于此接口的唯一實現(xiàn)類:ConfigurationImpl,這里就不用再做分析了,因為對于Validation這塊,咱們面向接口編程是完全沒有問題的~
準備好了Configuration后,下一步顯然就是configuration.buildValidatorFactory()來得到一個ValidatorFactory嘍,關(guān)于ValidatorFactory這塊的內(nèi)容,請聽下文分解~
總結(jié)該文講解是關(guān)于Bean Validation數(shù)據(jù)校驗,在現(xiàn)在Spring的高度封裝下,越來越少的人能夠主動去發(fā)現(xiàn)Java實現(xiàn)/標準了~
實際上Spring的強大并不是自己創(chuàng)造了多少輪子,而是它主要是帶來了更為簡單的抽象,從而減少樣板代碼、促進解耦、提高可單測性。因此對于有些常用的功能還是建議稍微了解多一點,做到心中有數(shù),運用起來也才會更加的游刃有余
若文章格式混亂,可點擊:原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接
==The last:如果覺得本文對你有幫助,不妨點個贊唄。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==
**若對技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群。
若群二維碼失效,請加wx號:fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會手動邀請入群**
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/75526.html
摘要:和上標注的約束都會被執(zhí)行注意如果子類覆蓋了父類的方法,那么子類和父類的約束都會被校驗。 每篇一句 沒有任何技術(shù)方案會是一種銀彈,任何東西都是有利弊的 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗:Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例【小家Spring】Spring方法級別數(shù)據(jù)校...
摘要:如果說要使用數(shù)據(jù)校驗,我十分相信小伙伴們都能夠使用,但估計大都是有個前提的環(huán)境。具體使用可參考小家讓支持對平鋪參數(shù)執(zhí)行數(shù)據(jù)校驗?zāi)J使用只能對進行校驗級聯(lián)校驗什么叫級聯(lián)校驗,其實就是帶校驗的成員里存在級聯(lián)對象時,也要對它完成校驗。 每篇一句 NBA里有兩大笑話:一是科比沒天賦,二是詹姆斯沒技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗:Java Bean Validation 2.0(...
摘要:就這樣借助相關(guān)約束注解,就非常簡單明了,語義清晰的優(yōu)雅的完成了方法級別入?yún)⑿r灧祷刂敌r灥男r?。但倘若是返回值校驗?zhí)行了即使是失敗了,方法體也肯定被執(zhí)行了只能哪些類型上提出這個細節(jié)的目的是約束注解并不是能用在所有類型上的。 每篇一句 在《深度工作》中作者提出這么一個公式:高質(zhì)量產(chǎn)出=時間*專注度。所以高質(zhì)量的產(chǎn)出不是靠時間熬出來的,而是效率為王 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校...
摘要:方案一借助對方法級別數(shù)據(jù)校驗的能力首先必須明確一點此能力屬于框架的,而部分框架。 每篇一句 在金字塔塔尖的是實踐,學(xué)而不思則罔,思而不學(xué)則殆(現(xiàn)在很多編程框架都只是教你碎片化的實踐) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗:Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例【小家Spr...
摘要:表達式語言依賴關(guān)系提供對變量插值的支持,允許在違規(guī)消息中使用表達式。這是當相應(yīng)屬性的值未通過驗證時通常會呈現(xiàn)的消息。任何違反對象中定義的約束的行為都將作為返回。案例結(jié)論本文重點介紹了標準的簡單傳遞,并使用注釋和說明了驗證的基礎(chǔ)知識。 案例概述 在這本文中,我們將介紹使用標準框架驗證Java bean的基礎(chǔ)知識 - JSR 380,也稱為Bean Validation 2.0。 當然,驗...
閱讀 2095·2021-10-11 10:59
閱讀 936·2021-09-23 11:21
閱讀 3572·2021-09-06 15:02
閱讀 1623·2021-08-19 10:25
閱讀 3380·2021-07-30 11:59
閱讀 2376·2019-08-30 11:27
閱讀 2589·2019-08-30 11:20
閱讀 2980·2019-08-29 13:15