经过上一节的,我们可以自己解析spel表达式。那么我现在的想法是,在注解的第一层aop中解析spel,然后将解析后的值设置到属性中,那么在之后的aop中就不用解析了。
找出注解中值存放位置
继续上一节的代码,在上一节的AOP中添加注解@Order(0),再新增一个注解,添加@Order(1)。注意order这个注解有坑的,最好先百度完再使用。
代码语言:txt复制@Component
@Aspect
@Order(0)
public class InterestResolveELAspect {
// resolve spel...
}
@Component
@Aspect
@Order(1)
public class InterestHandleAspect {
@Around("@annotation(anno)")
public Object invoked(ProceedingJoinPoint pjp, Interest anno) throws Throwable {
String key = anno.key();
String unless = anno.unless();
logger.info("call InterestHandleAspect.invoked, resolvedKey:[{}], resolvedUnless:[{}]"
, key, unless);
return pjp.proceed();
}
}
我们在上一节代码中的String keySpel = anno.key()打下断点。查看当前栈的变量。
发现注解的对象是一个Proxy的实例,Proxy的作用就是为java类生一个代理对象,有这个代理对象去调用真实方法,就像这样
代码语言:txt复制public interface A {
String func1();
}
public class B implements A {
@Override
public String func1() { //do something ... }
public String func2() { //do something ... };
}
public static void main(String ...args) {
B bInstance = new B();
B bProxy = Proxy.newProxyInstance(
B.class.getClassLoader(), // B 类的类加载器
B.class.getInterfaces(), // B 类所实现的接口,如果你想拦截B类的某个方法,必须让这个方法在某个接口中声明并让B类实现该接口
new InvocationHandler() { // 调用处理器,任何对 B类所实现的接口方法的调用都会触发此处理器
@Override
public Object invoke (Object proxy, // 这个是代理的实例,method.invoke时不能使用这个,否则会死循环
Method method, // 触发的接口方法
Object[] args // 此次调用该方法的参数
) throws Throwable {
System.out.println(String.format("调用 %s 之前", method.getName()));
/**
* 这里必须使用B类的某个具体实现类的实例,因为触发时这里的method只是一个接口方法的引用,
* 也就是说它是空的,你需要为它指定具有逻辑的上下文(bInstance)。
*/
Object obj = method.invoke(bInstance, args);
System.out.println(String.format("调用 %s 之后", method.getName()));
return obj; //返回调用结果
}
}
);
}
再回想注解实质上是一个接口,它本身没有逻辑,那么它的值存在什么地方呢?那么答案就是Proxy实例中了。
这个Proxy实例有一个类型为AnnotationInvocationHandler的变量h,我回到上面创建Proxy对象的代码中,Proxy.newProxyInstance()的第三个参数就是InvocationHandler,而这个变量h就是它的实现类。
继续往变量h里看,它有一个字段memberValues,是一个map,而在这个map中,我发现了注解值存放的位置。key为注解的属性名,value就是属性值。
修改注解值
找到了注解值存放位置,那么修改就简单了
代码语言:txt复制@Component
@Aspect
@Order(0)
public class InterestResolveELAspect {
@Around("@annotation(anno)")
public Object invoked(ProceedingJoinPoint pjp, Interest anno) throws Throwable {
// resolve spel
String key = resolve spel;
Boolean unless = resolve spel;
InvocationHandler h = Proxy.getInvocationHandler(anno);
Field hField = h.getClass().getDeclaredField("memberValues");
hField.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) hField.get(h);
memberValues.put("key", key);
memberValues.put("unless", unless.toString());
return pjp.proceed();
}
}
赶紧测试一下,看看InterestHandleAspect打印的内容是不是你想要的。。。。