Spring boot通過(guò)切面,實(shí)現(xiàn)超靈活的注解式數(shù)據(jù)校驗(yàn)過(guò)程
通過(guò)切面,實(shí)現(xiàn)超靈活的注解式數(shù)據(jù)校驗(yàn)
在企業(yè)系統(tǒng)的開發(fā)中,用戶表單輸入的場(chǎng)景是會(huì)經(jīng)常遇見的,如何讓數(shù)據(jù)校驗(yàn)脫離于業(yè)務(wù)代碼邏輯,誰(shuí)也不想在邏輯代碼里對(duì)字段逐一判斷。。。。
Spring MVC的校驗(yàn)方式
在使用Spring MVC時(shí)的時(shí)候,直接使用hibernate-validator的注解,如下:
public class User { private Long id; @NotBlank(message = "name不能為空") @Size(min = 5, max = 10, message = "字符在5到10個(gè)") private String name; private String des; @NotNull @Max(value = 3, message = "type 參數(shù)錯(cuò)誤") @Min(value = 0, message = "type 參數(shù)錯(cuò)誤") private Integer type; @Min(value = 0, message = "參數(shù)錯(cuò)誤, limit必須大于或等于0") private int limit; @Pattern(regexp = "^(true|false)$", message = "參數(shù)錯(cuò)誤, 參數(shù)isActive只能是true或者false") private String flag; // setters and getters
然后將User對(duì)象作為Controller的參數(shù),交給Spring MVC去幫你校驗(yàn)。
通過(guò)切面實(shí)現(xiàn)自己的注解式數(shù)據(jù)校驗(yàn)
這是一個(gè)SOA的微服務(wù)應(yīng)用,沒(méi)有controller和Spring MVC,當(dāng)然也沒(méi)有所謂的容器(Tomcat、Jetty),對(duì)于來(lái)自于client的調(diào)用,也要進(jìn)行參數(shù)校驗(yàn)。繼續(xù)基于hibernate-validator,
參看validator的官方文檔
引入依賴:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-cdi</artifactId> <version>5.4.1.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b08</version> </dependency>
這里需要引入spring boot和aop的一些知識(shí)點(diǎn),自行去網(wǎng)上google吧。我直接上代碼了,誰(shuí)叫我是代碼的搬運(yùn)工。
定義一個(gè)切面:
@Aspect //一個(gè)切面 @Configuration // spring boot 配置類 public class RequestParamValidAspect { private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private final ExecutableValidator methodValidator = factory.getValidator().forExecutables(); private final Validator beanValidator = factory.getValidator(); private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object [] params){ return methodValidator.validateParameters(obj, method, params); } private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) { return beanValidator.validate(bean); } @Pointcut("execution(* com.jiaobuchong.commodity.service.*.*(..))") public void soaServiceBefore(){} /* * 通過(guò)連接點(diǎn)切入 */ @Before("soaServiceBefore()") public void twiceAsOld1(JoinPoint point) { // 獲得切入目標(biāo)對(duì)象 Object target = point.getThis(); // 獲得切入方法參數(shù) Object [] args = point.getArgs(); // 獲得切入的方法 Method method = ((MethodSignature)point.getSignature()).getMethod(); // 校驗(yàn)以基本數(shù)據(jù)類型 為方法參數(shù)的 Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args); Iterator<ConstraintViolation<Object>> violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此處可以拋個(gè)異常提示用戶參數(shù)輸入格式不正確 System.out.println("method check---------" + violationIterator.next().getMessage()); } // 校驗(yàn)以java bean對(duì)象 為方法參數(shù)的 for (Object bean : args) { if (null != bean) { validResult = validBeanParams(bean); violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此處可以拋個(gè)異常提示用戶參數(shù)輸入格式不正確 System.out.println("bean check-------" + violationIterator.next().getMessage()); } } } } }
具體的Service
// DemoService.java public interface DemoService { void one(@NotNull(message = "不能為null") Integer a, @NotBlank String b); void two(@NotNull(message = "paramsVo不能為null") ParamsVo paramsVo, @NotNull(message = "go不能為null") String go); } // ParamsVo.java public class ParamsVo { @NotBlank(message = "不能為空") private String name; @NotBlank @Length(min = 2, max = 20, message = "不可以為空,最多20個(gè)字") private String desc; @NotNull @Valid // 需要加上@Valid注解,不然不會(huì)校驗(yàn)到Img對(duì)象 private List<Img> imgList; @NotNull(message = "length不能為null") @Range(min = 3, max = 100, message = "長(zhǎng)度范圍3-100") private Integer length; // omitted other code } public class Img { @NotNull(message = "img id 不能為null") private Long id; @NotBlank(message = "img name 不能為空") private String name; // omitted other code }
運(yùn)行DemoService:
@Autowired private DemoService demoService; @Test public void testGo() { demoService.one(null, ""); ParamsVo paramsVo = new ParamsVo(); List<Img> list = new ArrayList<>(); Img img = new Img(); list.add(img); paramsVo.setImgList(list); paramsVo.setDesc("你"); paramsVo.setLength(1); demoService.two(paramsVo, null); }
運(yùn)行結(jié)果:
method check———不能為空
method check———不能為null
method check———go不能為null
bean check——-img name 不能為空
bean check——-不能為空
bean check——-深度范圍3-100
bean check——-img id 不能為null
bean check——-不可以為空,最多20個(gè)字
這樣比Spring MVC的校驗(yàn)功能還強(qiáng)大了,
// Spring MVC中對(duì)下面這樣的校驗(yàn)是沒(méi)有作用的 void one(@NotNull(message = "不能為null") Integer a, @NotBlank String b);
經(jīng)過(guò)一番改造后,啥都支持了。而且獨(dú)立于業(yè)務(wù)邏輯,維護(hù)和新增校驗(yàn)都很方便,代碼量也變少了!
Spring boot aop注解數(shù)據(jù)權(quán)限校驗(yàn)
注解類
@Retention(RetentionPolicy.RUNTIME) public @interface DataAuthValid { //位置 public int index() default 0; //字段 id //public String id() default "id"; //字段 id public String orgId() default "org_id"; //mapper @SuppressWarnings("rawtypes") public Class<? extends Mapper> mapper(); }
AOP切面
@Aspect @Component @Order(1) public class DataAuthAop { private static String types = "java.lang.String,java.lang.Long,long"; @Before("@annotation(dataAuth)") public void beforeMethod(JoinPoint point,DataAuthValid dataAuth) throws Exception { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Map<String, Object> payloadMap = (Map<String, Object>) request.getAttribute("payloadMap"); Long companyid = Long.parseLong(payloadMap.get("companyid")+""); if(companyid != 1) { Object[] args = point.getArgs(); Object obj = args[dataAuth.index()]; String ids = null; String typeName = obj.getClass().getTypeName(); if(types.contains(typeName)) { ids = obj + ""; }else { Field[] fields = obj.getClass().getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); if("id".equals(f.getName())) { Long id = (Long) f.get(obj); ids = id + ""; } } } String[] idArr = ids.split(","); for (String id : idArr) { Class cla = dataAuth.mapper(); Mapper mapper = (Mapper) SpringBeanFactoryUtils.getApplicationContext().getBean(cla); Object object = mapper.selectByPrimaryKey(Long.valueOf(id)); Field field = obj.getClass().getDeclaredField(dataAuth.orgId()); field.setAccessible(true); Long orgId = (Long)field.get(obj); if(!companyid.equals(orgId)) { throw new RuntimeException(); } } } } }
使用
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java序列化對(duì)象根據(jù)不同配置動(dòng)態(tài)改變屬性名的方法
本文主要介紹了java序列化對(duì)象根據(jù)不同配置動(dòng)態(tài)改變屬性名的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05springmvc整合freemarker配置的詳細(xì)步驟
本篇文章主要介紹了springmvc整合freemarker的詳細(xì)步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05Intellij idea下使用不同tomcat編譯maven項(xiàng)目的服務(wù)器路徑方法詳解
今天小編就為大家分享一篇關(guān)于Intellij idea下使用不同tomcat編譯maven項(xiàng)目的服務(wù)器路徑方法詳解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02HashMap vs TreeMap vs Hashtable vs LinkedHashMap
這篇文章主要介紹了HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07SpringBoot實(shí)現(xiàn)發(fā)送郵件功能
這篇文章主要介紹了SpringBoot 發(fā)送郵件功能實(shí)現(xiàn),本文以163郵箱為例通過(guò)這個(gè)小案例給大家介紹,需要的朋友可以參考下2019-12-12Java基礎(chǔ)學(xué)習(xí)之字符緩沖流的應(yīng)用
這篇文章主要為大家詳細(xì)介紹了Java基礎(chǔ)中的字符緩沖流的相關(guān)應(yīng)用,例如復(fù)制Java文件等,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一2022-09-09淺析Spring boot 中 logback 配置<springPropert
這篇文章主要介紹了淺析Spring boot 中 logback 配置<springProperty> 讀取application.properties 中的屬性,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02詳解J2EE開發(fā)的網(wǎng)站部署到阿里云服務(wù)器的方法
這篇文章主要介紹了詳解J2EE開發(fā)的網(wǎng)站部署到阿里云服務(wù)器的方法,需要的朋友可以參考下2018-01-01