Spring boot通過切面,實(shí)現(xiàn)超靈活的注解式數(shù)據(jù)校驗(yàn)過程
通過切面,實(shí)現(xiàn)超靈活的注解式數(shù)據(jù)校驗(yàn)
在企業(yè)系統(tǒng)的開發(fā)中,用戶表單輸入的場(chǎng)景是會(huì)經(jīng)常遇見的,如何讓數(shù)據(jù)校驗(yàn)脫離于業(yè)務(wù)代碼邏輯,誰也不想在邏輯代碼里對(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)。
通過切面實(shí)現(xiàn)自己的注解式數(shù)據(jù)校驗(yàn)
這是一個(gè)SOA的微服務(wù)應(yīng)用,沒有controller和Spring MVC,當(dāng)然也沒有所謂的容器(Tomcat、Jetty),對(duì)于來自于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吧。我直接上代碼了,誰叫我是代碼的搬運(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(){}
/* * 通過連接點(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 = "長度范圍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)是沒有作用的 void one(@NotNull(message = "不能為null") Integer a, @NotBlank String b);
經(jīng)過一番改造后,啥都支持了。而且獨(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)文章
springboot使用spring-data-jpa操作MySQL數(shù)據(jù)庫
這篇文章主要介紹了springboot使用spring-data-jpa操作MySQL數(shù)據(jù)庫,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
Springboot整合mybatis-plus使用pageHelper進(jìn)行分頁(使用步驟)
PageHelper是一個(gè)MyBatis分頁插件,可以方便地實(shí)現(xiàn)數(shù)據(jù)庫查詢結(jié)果的分頁功能,在Maven或Gradle項(xiàng)目中引入依賴,并在配置文件中進(jìn)行配置,本文給大家介紹Springboot整合mybatis-plus使用pageHelper進(jìn)行分頁,感興趣的朋友跟隨小編一起看看吧2024-11-11
使用Jenkins自動(dòng)化構(gòu)建工具進(jìn)行敏捷開發(fā)
這篇文章主要為大家介紹了使用Jenkins自動(dòng)化構(gòu)建工具進(jìn)行敏捷開發(fā),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
java實(shí)現(xiàn)日歷應(yīng)用程序設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)日歷應(yīng)用程序設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
idea左側(cè)的commit框設(shè)置顯示出來方式
在IDEA中顯示左側(cè)的commit框,首先通過File-Settings-Version Control-Commit進(jìn)行設(shè)置,然后勾選Use non-modal commit interface完成2025-01-01

