0
点赞
收藏
分享

微信扫一扫

利用Spring AOP切面结合反射机制实现业务层参数校验

东方小不点 2022-03-12 阅读 52


1、自定义注解

package com.bobo.springboottest.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamValid {
String[] value() default {};
}

该注解可作用于方法和方法参数上。

2、定义AOP切面

package com.bobo.springboottest.aspect;

import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;

@Component
@EnableAspectJAutoProxy
@Aspect
public class ParamValidAspect {
// ParamValid和(*)之间必须要有空格。该切入点将拦截打了@ParamValid注解的方法或第一个参数打了@ParamValid注解的方法(一般业务层方法最好只有一个参数)
@Pointcut("@annotation(com.bobo.springboottest.aspect.ParamValid) || execution(* com.bobo.springboottest.service..*.*(@com.bobo.springboottest.aspect.ParamValid (*),..))")
public void pointcut(){
}

// 要想获取ProceedingJoinPoint,必须用Around通知,且ProceedingJoinPoint参数放第一位
@Around("pointcut() && args(param)")
public Object around(ProceedingJoinPoint joinPoint,Object param) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
System.out.println("执行环绕通知");
System.out.println("参数:"+ JSONObject.toJSONString(param));
// 获取注解参数
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
Class[] argsType = new Class[args.length];
for (int i = 0; i < args.length; i++) {
argsType[i] = args[i].getClass();
}
Method method = joinPoint.getTarget().getClass().getMethod(name,argsType);
ParamValid paramValid = method.getAnnotation(ParamValid.class);
if(null == paramValid){
// 这是是二维数组的原因是:一个方法有多个参数,而一个参数可以打多个注解
Annotation[][] paramAnnos = method.getParameterAnnotations();
if(null != paramAnnos && paramAnnos.length>0){
for (Annotation[] paramAnno : paramAnnos) {
for (Annotation anno : paramAnno) {
if(anno instanceof ParamValid){
paramValid = (ParamValid) anno;
break;
}
}
}
}
}
if(null != paramValid){
String[] requireds = paramValid.value();
System.out.println("必选参数为:"+ Arrays.asList(requireds).stream().collect(Collectors.joining(",")));
for (String fieldName : requireds) {
Object p = joinPoint.getArgs()[0];
Field f = p.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
Object fieldValue = f.get(p);
if(f.getType().equals(java.lang.String.class)){
if(StringUtils.isEmpty((String)fieldValue)){
return "缺失必要参数!";
}
}else if(f.getType().equals(java.util.Collection.class)){
if(CollectionUtils.isEmpty((java.util.Collection)fieldValue)){
return "缺失必要参数!";
}
}
}
}

Object res = null;
try {
// 带参数的proceed方法可以覆盖原来的参数
// res = joinPoint.proceed(new Object[]{new ParamValidReq("3","王五")});
res = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return res;
}
}

3、定义业务层

package com.bobo.springboottest.service;

import com.bobo.springboottest.aspect.ParamValid;
import com.bobo.springboottest.req.ParamValidReq;
import org.springframework.stereotype.Service;

@Service
public class ParamValidService {
public String doSomething(@ParamValid({"name"}) ParamValidReq paramValidReq){
return "success";
}
@ParamValid({"id","name"})
public String doSomething2( ParamValidReq paramValidReq){
return "success";
}
}

4、单元测试

@SpringBootTest
@RunWith(SpringRunner.class)
class SpringbootTestApplicationTests {
@Autowired
private ParamValidService paramValidService;
@Test
void test() throws SQLException {
ParamValidReq req1 = new ParamValidReq();
req1.setName("张三");
paramValidService.doSomething(req1);

ParamValidReq req2 = new ParamValidReq();
req2.setName("李四");
paramValidService.doSomething2(req2);
}
}

运行效果如下所示。

执行环绕通知
参数:{"name":"张三"}
必选参数为:name
res1:success
执行环绕通知
参数:{"name":"李四"}
必选参数为:id,name
res2:缺失必要参数!



举报

相关推荐

0 条评论