记一次AOP+反射动态修改注解值成功后注解没有生效
最近重新看了一下反射,突发奇想,在运行的时候在不同的方法上放入不同的注解值,然后获取到注解值进行修改。于是拿了hirbernate的@Validated来玩玩。
我的想法是自定义注解里面@Validated是空的{},然后我们通过获取不同接口(路由)上的@Validation注解值,然后修改进去@Validated里面。
如果看不懂修改注解值,可以参考一下这篇文章。获取出来的Annotation其实是个Proxy对象。https://blog.csdn.net/qq_39309348/article/details/110455235
上代码。
自定义注解
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Validated
public @interface Validation {
Class<?>[] value() default {};
interface PutGroup {
}
interface DeleteGroup {
}
interface ModifyGroup {
}
interface QueryGroup {
}
interface UpdateGroup extends DeleteGroup, ModifyGroup {
}
}
这是Test类
@Data
public class Test {
@NotNull(groups = {Validation.DeleteGroup.class})
String a;
@NotNull(groups = {Validation.DeleteGroup.class,Validation.ModifyGroup.class})
String b;
@NotNull(groups = {Validation.ModifyGroup.class})
String c;
}
这是测试接口
@RequestMapping("/test")
@SuppressWarnings("unchecked")
public String test(@Validation(Validation.ModifyGroup.class) Test test) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
// 这里通过反射获取到@Validation注解里面@Validated的注解值
Method method = TestController.class.getMethod("test", Test.class);
int parameterCount = method.getParameterCount();
Annotation[][] pas = method.getParameterAnnotations();
tag:
for (int i = 0; i < parameterCount; i++) {
for (Annotation annotation : pas[i]) {
//判断注解是否为指定类型
InvocationHandler ih = Proxy.getInvocationHandler(annotation);
Field type = ih.getClass().getDeclaredField("type");
type.setAccessible(true);
Class<Validation> clazz = (Class<Validation>) type.get(ih);
String name = clazz.getName();
String validationName = Validation.class.getName();
if (StringUtils.equals(validationName, name)) {
// 拿到注解上的@Validated注解
Validated validated = clazz.getAnnotation(Validated.class);
InvocationHandler h = Proxy.getInvocationHandler(validated);
Field memberValues = h.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) memberValues.get(h);
Class<?>[] value = validated.value();
if (value.length != 0) {
for (Class<?> aClass : value) {
System.out.println("修改后在test中的值为:" + aClass);
}
}else{
System.out.println("修改后没有值");
}
}
break tag;
}
}
return test.toString();
}
AOP拦截@RequestMapping
@Aspect
@Component
public class TimeAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void beforeTime() {
}
@Before("beforeTime()")
@SuppressWarnings("unchecked")
public void dynamicModify(JoinPoint joinPoint) throws NoSuchFieldException, IllegalAccessException {
//获取参数注解及个数
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
int parameterCount = method.getParameterCount();
Annotation[][] pas = method.getParameterAnnotations();
//遍历
tag:
for (int i = 0; i < parameterCount; i++) {
for (Annotation annotation : pas[i]) {
//判断注解是否为指定类型
InvocationHandler ih = Proxy.getInvocationHandler(annotation);
Field type = ih.getClass().getDeclaredField("type");
type.setAccessible(true);
Class<Validation> clazz = (Class<Validation>) type.get(ih);
String name = clazz.getName();
String validationName = Validation.class.getName();
if (StringUtils.equals(validationName, name)) {
// 拿到注解值
Validation validation = (Validation) annotation;
Class<?>[] valueCustomer = validation.value();
// 拿到注解上的@Validated注解
Validated validated = clazz.getAnnotation(Validated.class);
InvocationHandler h = Proxy.getInvocationHandler(validated);
Field memberValues = h.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) memberValues.get(h);
Class<?>[] value = validated.value();
if (value.length != 0) {
for (Class<?> aClass : value) {
System.out.println("修改前的值为:" + aClass);
}
}else{
System.out.println("修改前没有值");
}
map.put("value", valueCustomer);
Class<?>[] value1 = validated.value();
if (value1.length != 0) {
for (Class<?> aClass : value1) {
System.out.println("修改后的值为:" + aClass);
}
}
}
break tag;
}
}
}
}
运行后输出为:
这里很明显已经成功修改了。无论是前置通知还是方法中。可是如果修改成功,@Validated应该会生效才对的啊。
百思不得其解。那就干脆直接拿@Validated来试试吧。
修改一下
测试接口
@RequestMapping("/test")
@SuppressWarnings("unchecked")
public String test(@Validated(Validation.DeleteGroup.class) Test test) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
Method method = TestController.class.getMethod("test", Test.class);
int parameterCount = method.getParameterCount();
Annotation[][] pas = method.getParameterAnnotations();
//遍历
tag:
for (int i = 0; i < parameterCount; i++) {
for (Annotation annotation : pas[i]) {
//判断注解是否为指定类型
InvocationHandler ih = Proxy.getInvocationHandler(annotation);
Field type = ih.getClass().getDeclaredField("type");
type.setAccessible(true);
Class clazz = (Class) type.get(ih);
String name = clazz.getName();
String validatedName = Validated.class.getName();
System.out.println(name.equals(validatedName));
if (StringUtils.equals(validatedName, name)) {
// 拿到注解值
Validated validated = (Validated) annotation;
Class<?>[] valueCustomer = validated.value();
if (valueCustomer.length != 0) {
for (Class<?> aClass : valueCustomer) {
System.out.println("修改后在test方法中的注解值为:" + aClass);
}
}
}
break tag;
}
}
return test.toString();
}
AOP改为直接修改@Validated值,从Validation.DeleteGroup.class改成Validation.ModifyGroup.class
@Aspect
@Component
@Slf4j(topic = "log")
public class TimeAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void beforeTime() {
}
@Before("beforeTime()")
@SuppressWarnings("unchecked")
public void dynamicModify(JoinPoint joinPoint) throws NoSuchFieldException, IllegalAccessException {
//获取参数注解及个数
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
int parameterCount = method.getParameterCount();
Annotation[][] pas = method.getParameterAnnotations();
//遍历
tag:
for (int i = 0; i < parameterCount; i++) {
for (Annotation annotation : pas[i]) {
//判断注解是否为指定类型
InvocationHandler ih = Proxy.getInvocationHandler(annotation);
Field type = ih.getClass().getDeclaredField("type");
type.setAccessible(true);
Class clazz = (Class) type.get(ih);
String name = clazz.getName();
String validatedName = Validated.class.getName();
System.out.println(name.equals(validatedName));
if (StringUtils.equals(validatedName, name)) {
// 拿到注解值
Validated validated = (Validated) annotation;
Class<?>[] valueCustomer = validated.value();
if (valueCustomer.length != 0) {
for (Class<?> aClass : valueCustomer) {
System.out.println("修改前注解值为:" + aClass);
}
}
//修改注解值
Field memberValues = ih.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map map = (Map) memberValues.get(ih);
map.put("value", new Class[]{Validation.ModifyGroup.class});
Class<?>[] value = validated.value();
for (Class<?> aClass : value) {
System.out.println("修改后注解值为:" + aClass);
}
}
break tag;
}
}
}
}
测试结果为
结果发现这个@Validated的值又被改回来了。
结论:
反射只是相当于一个镜子,通过运行时类对象看到类的结构。我们通过@Validation修改的@Validated的值,仅仅是在@Validation这个类对象Class上的值。并不能修改到.class文件里面去。而@Validated去校验的时候,还是读取.class的值。所以看似修改了,实质还是没有起效果。
同样的,当我们直接使用@Validated的时候,虽然在增强中是修改了,但是一进行校验,@Validated底层又去.class文件读取。所以值就被修改回来了。
有错误的话希望路过的大佬可以指点一下