随笔分类
思考:
1.1 Spring为什么要去提供 AopContext (Aop上下文呢?)
看部分代码:源自 JdkDynamicAopProxy
这里根据变量 exposeProxy将 Aop暴露到上下文中去了
// true -> 需要将当前代理对象 "暴露"到 Aop上下文(AopContext)中去
// 暴露后, 应用程序便能获取到 proxy
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
这有什么应用场景呢?
以下图片,仅供个人理解参考:
简述:当 A中所有方法都需要被增强时,通过代理对象执行 getA()时,此时进行回调,去执行 JdkDynamicAopProxy中 invoke方法,在执行对应的拦截器后,会去调用目标对象来执行 getA(),此时 this是目标对象,而 getB()又在 getA()中会被执行,而此时的 getB()尚未被增强,这不就出现问题了吗?
因此,这里就需要将 AopProxy暴露到 AopContext中去,将来执行 getB()时,实际上还是 $proxy0(或其它 name),此时就会触发另一个回调,来达到增强的逻辑.
1.2 目标方法若是返回 this,此时 Spring需要做些什么处理呢?
源码即诠释了一切,当然,注释更是恰到起初!
JdkDynamicAopProxy.invoke
// true - 目标方法返回了目标对象, 目标对象不能直接返回!
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
// 这里来执行一个替换操作, 返回代理对象, 而不是目标对象
// 为什么不能返回目标对象呢?
// 如果直接返回了目标对象, 那么程序对目标对象的操作我们是无法进行控制的, 如果基于目标对象去调用了需要增强的其他方法
// 那么目标方法将得不到增强, 因此, 当 invoke方法(即增强的目标方法返回目标对象 this)时, 此时需要进行替换
retVal = proxy;
}
验证:
1.1
首先来看一个问题:
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫猫吃 猫粮");
// 另一个目标对象会被增强吗?
go();
}
@Override
public void go() {
System.out.println("猫猫跑 ~");
}
}
在 eat()中调用了 go(),go()会被得到增强吗?
不会的,这属于方法的内部调用,实际上由目标对象来进行调用的,那怎么来解决此 痛点
?
Spring提供了 AopContext(Aop上下文),方法调用前将代理对象暴露出去,由代理对象来去执行内部目标方法(这就需要我们手动来改些代码了),当然执行结束后,invoke()中也进行了恢复
见代码:
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫猫吃 猫粮");
// 从 Aop上下文中获取代理对象 - 前提:代理对象允许暴露到 Aop上下文中去
Animal proxy = (Animal) AopContext.currentProxy();
proxy.go();
}
@Override
public void go() {
System.out.println("猫猫跑 ~");
}
}
public class Thinking {
public static void main(String[] args) {
Animal cat = new Cat();
ProxyFactory proxyFactory = new ProxyFactory(cat);
proxyFactory.addAdvice(new MethodInterceptor01());
// 设置允许暴露代理对象到 Aop上下文中去
proxyFactory.setExposeProxy(true);
Animal proxy = (Animal) proxyFactory.getProxy();
proxy.eat();
}
}
运行结果:
methodInterceptor01 start
猫猫吃 猫粮
methodInterceptor01 start
猫猫跑 ~
methodInterceptor01 end
methodInterceptor01 end
1.2
@Override
public Animal returnThis() {
// 这里能返回目标对象自身么?
return this;
}
// 这里来验证下 go()是否被增强即可
proxy.returnThis().go();
运行结果:
methodInterceptor01 start
methodInterceptor01 end
methodInterceptor01 start
猫猫跑 ~
methodInterceptor01 end
由此可见,go方法也被增强了,Aop动态代理增强在这方面做得还是挺不错的!