記一次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檔案讀取,所以值就被修改回來了,
有錯誤的話希望路過的大佬可以指點一下
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229974.html
標籤:java
上一篇:爬蟲百戰穿山甲(三)
