参数校验
自定义注解+切面aop技术
使用自定义注解结合aop技术实现参数校验,通过把注解作为一个标记,根据标记来进行参数校验。
- 自定义注解
- 定义校验类型的枚举类
- 定义切面类,使用前置增强
package wnan.explore.annotations;
import wnan.explore.enumerations.ArgumentVerifyTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*****UTF-8*****
* Description: 参数校验注解
* Author: wnan
* Create Date: 2024/10/8 22:47
* Proverbs: 吃得苦中苦,方为人上人
*/
// 注解的生命周期即,注解的保留策略,注解保留在源代码、编译后的字节码文件中、运行时可以通过反射机制读取。
@Retention(RetentionPolicy.RUNTIME)
// 注解可以应用的地方 是写在方法、类、属性
@Target(ElementType.PARAMETER)
public @interface ArgumentVerify {
boolean require() default true;
ArgumentVerifyTypeEnum type() default ArgumentVerifyTypeEnum.OTHER;
}
package wnan.explore.enumerations;
/*****UTF-8*****
* Description: 参数校验类型
* Author: wnan
* Create Date: 2024/10/8 23:02
* Proverbs: 吃得苦中苦,方为人上人
*/
public enum ArgumentVerifyTypeEnum {
EMAIL(1,"邮件"),PHONE(2,"电话"),OTHER(2,"其他")
; // 这个分号,一定要有,就算没有枚举实例也要写
private final int type;
private final String typeInfo;
ArgumentVerifyTypeEnum(int type, String typeInfo) {
this.type = type;
this.typeInfo = typeInfo;
}
public int getType() {
return type;
}
public String getTypeInfo() {
return typeInfo;
}
}
package wnan.explore.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import wnan.explore.annotations.ArgumentVerify;
import wnan.explore.enumerations.ArgumentVerifyTypeEnum;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/*****UTF-8*****
* Description: 参数校验aop切面类
* Author: wnan
* Create Date: 2024/10/8 23:10
* Proverbs: 吃得苦中苦,方为人上人
*/
@Aspect
@Component("argumentAspects")
public class ArgumentAspects {
// 定义切点
@Pointcut("execution(void wnan.explore.service.ArgumentVerifyServiceImpl.argumentVerifyFun1(..))")
public void argumentToVerify() {
}
// 执行方法
@Before("argumentToVerify()")
public void verifyBeforeException(JoinPoint joinPoint) throws Exception {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 参数注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
// 参数
Parameter[] parameters = method.getParameters();
// 参数值
Object[] args = joinPoint.getArgs();
// 方法一
for (int i = 0; i < parameterAnnotations.length; i++) {
for (int j = 0; j < parameterAnnotations[i].length; j++) {
if (parameterAnnotations[i][j].annotationType().equals(ArgumentVerify.class)) {
// 获得具体注解方法一
// ArgumentVerify annotation = (ArgumentVerify) parameterAnnotations[i][j];
// 获得具体注解方法2
ArgumentVerify annotation = parameters[i].getAnnotation(ArgumentVerify.class);
ArgumentVerifyTypeEnum type = annotation.type();
switch (type) {
case EMAIL: {
System.out.println("邮件校验");
System.out.println(args[i]);
break;
}
case PHONE: {
System.out.println("电话校验");
System.out.println(args[i]);
break;
}
default: {
System.out.println("其他校验");
System.out.println(args[i]);
}
}
}
}
}
}
}
方法二,就是不使用if判断:
IntStream.range(0, parameterAnnotations.length) .filter(i -> Arrays.stream(parameterAnnotations[i]) .anyMatch(annotation -> annotation.annotationType().equals(ArgumentVerify.class))) .forEach(i -> { ArgumentVerify annotation = parameters[i].getAnnotation(ArgumentVerify.class); ArgumentVerifyTypeEnum type = annotation.type(); // switch处理 });
package wnan.explore.service;
import org.springframework.stereotype.Service;
import wnan.explore.annotations.ArgumentVerify;
import wnan.explore.enumerations.ArgumentVerifyTypeEnum;
/*****UTF-8*****
* Description: 参数校验动作类
* Author: wnan
* Create Date: 2024/10/10 16:24
* Proverbs: 吃得苦中苦,方为人上人
*/
@Service
public class ArgumentVerifyServiceImpl {
public void argumentVerifyFun1(@ArgumentVerify(require = true, type = ArgumentVerifyTypeEnum.OTHER) int type,
String phone,
@ArgumentVerify(require = true, type = ArgumentVerifyTypeEnum.EMAIL) String email) {
System.out.println("====>>>> argumentVerifyFun1");
}
}
测试:
@Autowired
private ArgumentVerifyServiceImpl argumentVerifyService;
@Test
void argumentVerifyFun1() {
// aop参数校验
argumentVerifyService.argumentVerifyFun1(1, "123 589 5621", "1235@qq.com");
}
运行结果:
cglib动态代理+自定义注解
步骤:
- 自定义注解,注解用于方法参数上面
- 定义校验类型的枚举类
- 实现cglib的MethodInterceptor接口,重写intercept()方法,完成校验规则
- 进行cglib代理的设置。
我觉得不能使用jdk动态代理来实现,jdk代理是基于接口来实现,但是我的注解是在实现类里。
package wnan.explore.service;
import org.springframework.stereotype.Service;
import wnan.explore.annotations.ArgumentVerify;
import wnan.explore.enumerations.ArgumentVerifyTypeEnum;
/*****UTF-8*****
* Description: 参数校验动作类
* Author: wnan
* Create Date: 2024/10/10 16:24
* Proverbs: 吃得苦中苦,方为人上人
*/
@Service
public class ArgumentVerifyServiceImpl {
public int argumentVerifyFun2(@ArgumentVerify(require = true, type = ArgumentVerifyTypeEnum.OTHER) int type,
String phone,
@ArgumentVerify(require = true, type = ArgumentVerifyTypeEnum.EMAIL) String email) {
System.out.println("====>>>> argumentVerifyFun2");
return 0;
}
}
cglib代理
package wnan.explore.proxy;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/*****UTF-8*****
* Description: cglib实现动态代理参数校验
* Author: wnan
* Create Date: 2024/10/11 23:23
* Proverbs: 吃得苦中苦,方为人上人
*/
public class ArgmentVerifyCGLIB implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// System.out.println(obj); //不要输出 erro:java.lang.StackOverflowError 异常通常表示栈内存溢出,这通常发生在递归调用太深或者方法调用形成了无限循环时。在你提供的代码中,异常发生在 ArgmentVerifyCGLIB.intercept 方法中,特别是当你尝试打印 obj 对象时,这个对象实际上是被代理的对象。
String name = method.getName();
System.out.println(name);
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Arrays.stream(parameterAnnotations)
.forEach(annotations -> Arrays.stream(annotations)
.forEach(System.out::println));
// 参数校验的行为在这实现,同上一个方法
Object invoke_ = proxy.invokeSuper(obj, args);
return invoke_;
}
public <T> T createProxyInstance(T t) {
Enhancer enhancer = new Enhancer();
// 获得aop代理后的原始类型
Class<?> aClass = AopProxyUtils.ultimateTargetClass(t);
// 设置代理类的父类
enhancer.setSuperclass(aClass);
// 当执行被代理方法时要回调的类
enhancer.setCallback(this);
Object o = enhancer.create();
return (T) o;
}
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {}所有生成的代理方法都调用此方法而不是原始方法。可以使用 Method 对象通过正常反射调用原始方法,也可以使用 MethodProxy(更快)调用原始方法。 形参:
- obj – “this”,增强的对象
- method – 拦截方法
- args – 参数数组;原始类型被包装
- proxy ― 用于调用 super (非拦截方法);可以根据需要多次调用
@Autowired
private ArgumentVerifyServiceImpl var3; // 如果是接口类型也是没有区别,也可以实现
@Test
void t11(){
System.out.println("Bean对应的类型"+var3.getClass());
Class<?> aClass = AopProxyUtils.ultimateTargetClass(var3);
System.out.println("Bean对应代理前的类型"+aClass);
ArgmentVerifyCGLIB var1= new ArgmentVerifyCGLIB();
ArgumentVerifyServiceImpl proxyInstance = var1.createProxyInstance(var3);
proxyInstance.argumentVerifyFun2(1, "123 589 5621", "1235@qq.com");
}
运行结果: