Spring boot通過切面,實現(xiàn)超靈活的注解式數(shù)據(jù)校驗過程
通過切面,實現(xiàn)超靈活的注解式數(shù)據(jù)校驗
在企業(yè)系統(tǒng)的開發(fā)中,用戶表單輸入的場景是會經(jīng)常遇見的,如何讓數(shù)據(jù)校驗脫離于業(yè)務(wù)代碼邏輯,誰也不想在邏輯代碼里對字段逐一判斷。。。。
Spring MVC的校驗方式
在使用Spring MVC時的時候,直接使用hibernate-validator的注解,如下:
public class User { private Long id; @NotBlank(message = "name不能為空") @Size(min = 5, max = 10, message = "字符在5到10個") private String name; private String des; @NotNull @Max(value = 3, message = "type 參數(shù)錯誤") @Min(value = 0, message = "type 參數(shù)錯誤") private Integer type; @Min(value = 0, message = "參數(shù)錯誤, limit必須大于或等于0") private int limit; @Pattern(regexp = "^(true|false)$", message = "參數(shù)錯誤, 參數(shù)isActive只能是true或者false") private String flag; // setters and getters
然后將User對象作為Controller的參數(shù),交給Spring MVC去幫你校驗。
通過切面實現(xiàn)自己的注解式數(shù)據(jù)校驗
這是一個SOA的微服務(wù)應(yīng)用,沒有controller和Spring MVC,當(dāng)然也沒有所謂的容器(Tomcat、Jetty),對于來自于client的調(diào)用,也要進(jìn)行參數(shù)校驗。繼續(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的一些知識點(diǎn),自行去網(wǎng)上google吧。我直接上代碼了,誰叫我是代碼的搬運(yùn)工。
定義一個切面:
@Aspect //一個切面 @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(){} /* * 通過連接點(diǎn)切入 */ @Before("soaServiceBefore()") public void twiceAsOld1(JoinPoint point) { // 獲得切入目標(biāo)對象 Object target = point.getThis(); // 獲得切入方法參數(shù) Object [] args = point.getArgs(); // 獲得切入的方法 Method method = ((MethodSignature)point.getSignature()).getMethod(); // 校驗以基本數(shù)據(jù)類型 為方法參數(shù)的 Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args); Iterator<ConstraintViolation<Object>> violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此處可以拋個異常提示用戶參數(shù)輸入格式不正確 System.out.println("method check---------" + violationIterator.next().getMessage()); } // 校驗以java bean對象 為方法參數(shù)的 for (Object bean : args) { if (null != bean) { validResult = validBeanParams(bean); violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此處可以拋個異常提示用戶參數(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個字") private String desc; @NotNull @Valid // 需要加上@Valid注解,不然不會校驗到Img對象 private List<Img> imgList; @NotNull(message = "length不能為null") @Range(min = 3, max = 100, message = "長度范圍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個字
這樣比Spring MVC的校驗功能還強(qiáng)大了,
// Spring MVC中對下面這樣的校驗是沒有作用的 void one(@NotNull(message = "不能為null") Integer a, @NotBlank String b);
經(jīng)過一番改造后,啥都支持了。而且獨(dú)立于業(yè)務(wù)邏輯,維護(hù)和新增校驗都很方便,代碼量也變少了!
Spring boot aop注解數(shù)據(jù)權(quá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(); } } } } }
使用
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
java序列化對象根據(jù)不同配置動態(tài)改變屬性名的方法
本文主要介紹了java序列化對象根據(jù)不同配置動態(tài)改變屬性名的方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05springmvc整合freemarker配置的詳細(xì)步驟
本篇文章主要介紹了springmvc整合freemarker的詳細(xì)步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Intellij idea下使用不同tomcat編譯maven項目的服務(wù)器路徑方法詳解
今天小編就為大家分享一篇關(guān)于Intellij idea下使用不同tomcat編譯maven項目的服務(wù)器路徑方法詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02HashMap vs TreeMap vs Hashtable vs LinkedHashMap
這篇文章主要介紹了HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相關(guān)知識,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-07-07Java基礎(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ì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02詳解J2EE開發(fā)的網(wǎng)站部署到阿里云服務(wù)器的方法
這篇文章主要介紹了詳解J2EE開發(fā)的網(wǎng)站部署到阿里云服務(wù)器的方法,需要的朋友可以參考下2018-01-01