這篇文章主要介紹“spring注解校驗原理是什么”,在日常操作中,相信很多人在spring注解校驗原理是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”spring注解校驗原理是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
目前創(chuàng)新互聯(lián)建站已為1000+的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機、綿陽服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計、橋東網(wǎng)站維護等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
一、介紹
關(guān)于參數(shù)合法性驗證的重要性就不多說了,即使前端對參數(shù)做了基本驗證以外,后端依然還需要進行驗證,以防不合規(guī)的數(shù)據(jù)直接進入后端,嚴(yán)重的甚至?xí)斐上到y(tǒng)直接崩潰!
本文結(jié)合自己在項目中的實際使用經(jīng)驗,主要以實用為主,對數(shù)據(jù)合法性驗證做一次總結(jié),不了解的朋友可以學(xué)習(xí)一下,同時可以立馬實踐到項目上去。
下面我們通過幾個示例來演示如何判斷參數(shù)是否合法,不多說直接開擼!
二、斷言驗證
對于參數(shù)的合法性驗證,最初的做法比較簡單,自定義一個異常類。
public class CommonException extends RuntimeException { /**錯誤碼*/ private Integer code; /**錯誤信息*/ private String msg; //...set/get public CommonException(String msg) { super(msg); this.msg = msg; } public CommonException(String msg, Throwable cause) { super(msg, cause); this.msg = msg; } }
當(dāng)判斷某個參數(shù)不合法的時候,直接拋異常!
@RestController public class HelloController { @RequestMapping("/upload") public void upload(MultipartFile file) { if (file == null) { throw new CommonException("請選擇上傳文件!"); } //..... } }
然后寫一個統(tǒng)一異常攔截器,對拋異常的程序進行處理。
這種做法比較直觀,如果當(dāng)前參數(shù)既要判斷是否為空,又要判斷長度是否超過最大長度的時候,代碼就顯得有點多了!
于是,程序界的大佬想到了一個更加優(yōu)雅又能節(jié)省代碼的方式,創(chuàng)建一個斷言類工具類,專門用來判斷參數(shù)的是否合法,如果不合法,就拋異常!
/** * 斷言工具類 */ public abstract class LocalAssert { public static void isTrue(boolean expression, String message) throws CommonException { if (!expression) { throw new CommonException(message); } } public static void isStringEmpty(String param, String message) throws CommonException{ if(StringUtils.isEmpty(param)) { throw new CommonException(message); } } public static void isObjectEmpty(Object object, String message) throws CommonException { if (object == null) { throw new CommonException(message); } } public static void isCollectionEmpty(Collection coll, String message) throws CommonException { if (coll == null || (coll.size() == 0)) { throw new CommonException(message); } } }
當(dāng)我們需要對參數(shù)進行驗證的時候,直接通過這個類就可以完成基本操作,方式如下:
@RestController public class HelloController { @RequestMapping("/save") public void save(String name, String email) { LocalAssert.isStringEmpty(name, "用戶名不能為空!"); LocalAssert.isStringEmpty(email, "郵箱不能為空!"); //..... } }
相比上個步驟,當(dāng)要判斷的參數(shù)比較多時,代碼明顯簡潔多了!
類似這樣的工具類,spring也提供了一個名為Assert的斷言工具類,在開發(fā)的時候,可以直接使用!
三、注解驗證
使用注解對數(shù)據(jù)進行合法性驗證,可以說是 java 界一項非常偉大的創(chuàng)新,使用這種方式不僅使的代碼變得很簡潔,而且閱讀起來非常令人賞心悅目!
3.1、依賴包引入
下面我們一起來看看具體的實踐方式,以Spring Boot工程為例,如果需要使用注解校驗,直接引入spring-boot-starter-web依賴包即可,會自動將注解驗證相關(guān)的依賴包打入工程!
org.springframework.boot spring-boot-starter-web
下面在創(chuàng)建實體類的時候,還會用到lombok插件,因此還需要引入lombok依賴包!
org.projectlombok lombok 1.18.4 provided
如果是普通的Java工程,引入以下幾個依賴包即可!
org.hibernate.validator hibernate-validator 6.0.9.Final javax.el javax.el-api 3.0.0 org.glassfish.web javax.el 2.2.6
3.2、注解校驗請求對象
緊接著我們來創(chuàng)建一個實體User,用于模擬用戶注冊時的請求實體對象!
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class User { @NotBlank(message = "用戶名不能為空!") private String userName; @Email(message = "郵箱格式不正確") @NotBlank(message = "郵箱不能為空!") private String email; @NotBlank(message = "密碼不能為空!") @Size(min = 8, max = 16,message = "請輸入長度在8~16位的密碼") private String userPwd; @NotBlank(message = "確認密碼不能為空!") private String confirmPwd; }
在web層創(chuàng)建一個register()注冊接口方法,同時在請求參數(shù)上添加@Valid,如下:
@RestController public class UserController { @RequestMapping("/register") public boolean register(@RequestBody @Valid User user){ if(!user.getUserPwd().equals(user.getConfirmPwd())){ throw new CommonException("確認密碼與密碼不相同,請確認!"); } //業(yè)務(wù)處理... return true; } }
最后自定義一個異常全局處理器,用于處理異常消息,如下:
@Slf4j @Configuration public class GlobalWebMvcConfig implements WebMvcConfigurer { /** * 統(tǒng)一異常處理 * @param resolvers */ @Override public void configureHandlerExceptionResolvers(Listresolvers) { resolvers.add(new HandlerExceptionResolver() { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) { log.error("【統(tǒng)一異常攔截】請求出現(xiàn)異常,內(nèi)容如下:",e); ModelAndView mv = new ModelAndView(new MappingJackson2JsonView()); String uri = request.getRequestURI(); if(e instanceof CommonException){ //CommonExecption為自定義異常類拋出的異常 printWrite(((CommonException) e).getMsg(),((CommonException) e).getData(), uri, mv); } else if(e instanceof MethodArgumentNotValidException){ //MethodArgumentNotValidException為注解校驗異常類 //獲取注解校驗異常信息 String error = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage(); printWrite(error,null, uri, mv); } else { printWrite(e.getMessage(),null, uri, mv); } return mv; } }); } /** * 異常封裝相應(yīng)結(jié)果 * @param object */ private void printWrite(String msg, Object object, String uri, ModelAndView mv){ ResResult resResult = new ResResult(uri, object); if(msg != null){resResult.setMsg(msg);} if(log.isDebugEnabled()){ log.debug("【response】異常輸出結(jié)果:" + JSONObject.toJSONString(resResult, SerializerFeature.WriteMapNullValue)); } Map resultMap = BeanToMapUtil.beanToMap(resResult); mv.addAllObjects(resultMap); } }
下面我們啟動項目,使用postman來測試一把,看看效果如何?
測試字段是否為空
測試郵箱是否合法
測試密碼長度是否符合要求
測試密碼與確認密碼是否相同
3.3、注解校驗請求參數(shù)
上面我們介紹了請求對象的驗證方式,那如果直接在方法上對請求參數(shù)進行驗證是否同樣有效呢?
為了眼見為實,下面我們就來模擬在方法上對請求參數(shù)進行驗證,看看結(jié)果如何。
新建一個查詢接口query,如下
@RestController public class UserController { @PostMapping("/query") public boolean query(@RequestParam("userId") @Valid @NotBlank(message = "用戶ID不能為空") String userId ){ return true; } }
使用postman請求試一試,默認給userId參數(shù)為null,結(jié)果如下:
很清晰的看到,query()方法中的參數(shù)注解驗證無效!
當(dāng)我們在UserController類上加上@Validated注解!
@RestController @Validated public class UserController { @PostMapping("/query") public boolean query(@RequestParam("userId") @Valid @NotBlank(message = "用戶ID不能為空") String userId ){ return true; } }
使用postman請求再試一試,結(jié)果如下!
很清晰的看到,注解進行了驗證,同時還拋出異常ConstraintViolationException!
@Validated參數(shù)作用于類上時,表示告訴Spring可以對方法中請求參數(shù)進行校驗!
所有在實際開發(fā)的時候,我們可以使用@Validated和@Valid注解的組合來對方法中的請求參數(shù)和請求對象進行校驗!
同時,@Validated和@Valid注解不僅僅只是驗證控制器級別,可以驗證任何Spring組件,例如Service層方法入?yún)⒌尿炞C!
@Service @Validated public class UserService { public void saveUser(@Valid User user){ //dao插入 } }
3.4、自定義注解驗證
默認的情況下,依賴包已經(jīng)給我們提供了非常多的校驗注解,如下!
JSR提供的校驗注解!
Hibernate Validator提供的校驗注解
但是某些情況,例如性別這個參數(shù)可能需要我們自己去驗證,同時我們也可以自定義一個注解來完成參數(shù)的校驗,實現(xiàn)方式如下!
新創(chuàng)建一個Sex注解,其中SexValidator類指的是具體的參數(shù)驗證類
@Target({FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = SexValidator.class) @Documented public @interface Sex { String message() default "性別值不在可選范圍內(nèi)"; Class>[] groups() default {}; Class extends Payload>[] payload() default {}; }
SexValidator類,實現(xiàn)自ConstraintValidator接口
public class SexValidator implements ConstraintValidator{ @Override public boolean isValid(String value, ConstraintValidatorContext context) { Set sexSet = new HashSet (); sexSet.add("男"); sexSet.add("女"); return sexSet.contains(value); } }
最后在User實體類上加入一個性別參數(shù),使用自定義注解進行校驗!
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class User { @NotBlank(message = "用戶名不能為空!") private String userName; @Email(message = "郵箱格式不正確") @NotBlank(message = "郵箱不能為空!") private String email; @NotBlank(message = "密碼不能為空!") @Size(min = 8, max = 16,message = "請輸入長度在8~16位的密碼") private String userPwd; @NotBlank(message = "確認密碼不能為空!") private String confirmPwd; /** * 自定義注解校驗 */ @Sex(message = "性別輸入有誤!") private String sex; }
使用postman來請求試一試,結(jié)果如下!
不傳sex參數(shù)
很清晰的看到,已經(jīng)生效!
3.5、手動進行注解校驗
某些時候呢,假如有100個類需要用到校驗注解,此時我們可能在每個類會加上注解@Validated或者@Valid,再增加100個這樣的類,就會造成很多大量的重復(fù)工作。
而此時,我們的訴求是想對有校驗注解的實體類進行全局參數(shù)驗證!
解決辦法就會用到Validator提供的手動注解校驗證工具類,實現(xiàn)方法如下!
新建一個注解驗證工具類
/** * 注解校驗工具類 */ public class ValidatorUtils { /** * 獲取對象中所有注解校驗證異常信息 * @param object * @return */ public static String validated(Object object){ ListerrorMessageList = new ArrayList<>(); //獲取注解校驗工廠 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Set > violations = validator.validate(object); for (ConstraintViolation
使用ValidatorUtils工具類,對參數(shù)進行驗證
@Test public void testUser(){ User user = new User(); System.out.println(ValidatorUtils.validated(user)); }
執(zhí)行之后,結(jié)果如下!
[郵箱不能為空!, 用戶名不能為空!, 密碼不能為空!, 確認密碼不能為空!, 性別輸入有誤!]
當(dāng)然你還可以對ValidatorUtils類進行改造,當(dāng)有異常信息的時候,直接拋異常!
同時,你還可以通過@Autowired直接注入的方式來獲取Validator對象!
@Autowired Validator validator
3.6、spring 注解校驗原理
如果你對springmvc的方法參數(shù)解析器(HandlerMethodArgumentResolver)了解的話,就可能會想到參數(shù)校驗這塊肯定是在對應(yīng)的方法參數(shù)解析器里執(zhí)行的。
直接定位到resolveArgument這個方法,先通過WebDataBinder進行入?yún)傩越壎?,然后再進行校驗!
validateIfApplicable方法邏輯,會遍歷當(dāng)前參數(shù)methodParam所有的注解,如果注解是@Validated或者注解的名字以Valid開頭,則使用WebDataBinder對象執(zhí)行校驗邏輯。
方法參數(shù)解析器只針對接口請求時入?yún)⑦M行驗證,如果想對任何組件中方法進行注解校驗,似乎還缺了點什么!
而當(dāng)需要對一個類中的方法參數(shù)使用注解校驗時,在類上加上@Validated就是為了告訴Spring去校驗方法參數(shù)!
底層核心是通過切面代理類并配合MethodValidationPostProcessor這個后置處理器進行處理!
到此,關(guān)于“spring注解校驗原理是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
分享標(biāo)題:spring注解校驗原理是什么
鏈接分享:http://weahome.cn/article/pooedo.html