众所周知,如果没有AspectJ,bean方法的自调用在Spring中是行不通的。
例如,请看这个问题。
class MyClass {
@Autowired
private MyClass self; // actually a MyProxy instance
@Transactional // or any other proxy magic
public void myMethod() {}
public void myOtherMethod() {
this.myMethod(); // or self.myMethod() to avoid self-invokation problem
}
}
class MyProxy extends MyClass { // or implements MyInterface if proxyMode is not TARGET_CLASS and MyClass also implements MyInterface
private final MyClass delegate;
@Override
public void myMethod() {
// some proxy magic: caching, transaction management etc
delegate.myMethod();
// some proxy magic: caching, transaction management etc
}
@Override
public void myOtherMethod() {
delegate.myOtherMethod();
}
}
使用以下代码:
public void myOtherMethod() {
this.myMethod();
}
this.mymethod()
将绕过代理(所以所有@transactional
或@cacheable
魔术),因为它只是内部委托的调用...因此,我们应该在myclass
中注入一个myclass
bean(实际上是myproxy
实例),并调用self.mymethod()
。这是可以理解的。
但是为什么代理是以这种方式实现的呢?为什么它不只是扩展目标类,重写所有公共方法并调用super
而不是delegate
?像这样:
class MyProxy extends MyClass {
// private final MyClass delegate; // no delegate
@Override
public void myMethod() {
// some proxy magic: caching, transaction management etc
super.myMethod();
// some proxy magic: caching, transaction management etc
}
@Override
public void myOtherMethod() {
super.myOtherMethod();
}
}
它应该可以解决自调用问题,即this.mymethod()
绕过代理,因为在本例中,从MyClass.myothermethod()
调用的this.mymethod()
将调用overriden子方法(MyProxy.mymethod()
()。
所以,我的主要问题是为什么不这样实施?
您认为Spring AOP对其代理使用委托是正确的。这也被记录在案。
使用CGLIB,理论上可以使用proxy.invokesuper()
来实现您想要的效果,即自调用由代理的方法拦截器实现的方面注册(我在这里使用的是Spring的嵌入式CGLIB,因此是包名):
package spring.aop;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class SampleClass {
public void x() {
System.out.println("x");
y();
}
public void y() {
System.out.println("y");
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
if(method.getDeclaringClass() == Object.class)
return proxy.invokeSuper(obj, args);
System.out.println("Before proxy.invokeSuper " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After proxy.invokeSuper " + method.getName());
return result;
}
});
SampleClass proxy = (SampleClass) enhancer.create();
proxy.x();
}
}
控制台日志:
Before proxy.invokeSuper x
x
Before proxy.invokeSuper y
y
After proxy.invokeSuper y
After proxy.invokeSuper x
>
选项1:每个方面都有自己的代理。除非您根据方面优先级将代理嵌套到彼此中,否则这显然是行不通的。但是将它们嵌套在一起意味着继承,即一个代理必须从另一个从外到内继承。尝试代理一个CGLIB代理,它不工作,你会得到异常。此外,CGLIB代理非常昂贵,并且使用perm-gen内存,请参见本CGLIB入门中的描述。
选项2:使用组合而不是继承。构图更加灵活。如果有一个代理,您可以根据需要向其注册方面,那么就可以解决继承问题,但也意味着委托:代理注册方面,并在运行时按照正确的顺序在实际对象的代码执行之前/之后调用它们的方法(如果@around
建议从未调用procede()
)的话,则不调用这些方法)。请参阅Spring手册中关于将方面手动注册到代理的示例:
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();
至于Spring开发人员为什么选择这种方法,以及是否可以使用one-proxy方法,但仍然要确保自调用像上面我的小CGLIB示例“Logging Aspect”中那样工作,我只能猜测。您也许可以在开发人员邮件列表上询问他们,或者查看源代码。原因可能是CGLIB代理的行为应该与默认的Java动态代理相似,以便在两者之间无缝地切换接口类型。也许原因是另一个。
我在评论中无意粗鲁,只是直截了当,因为您的问题真的不适合StackOverflow,因为它不是一个可以有人找到解决方案的技术问题。这是一个历史上的设计问题,本质上是哲学问题,因为使用AspectJ,在实际问题之下已经存在一个技术问题(自调用)的解决方案。但是,也许您仍然想深入研究Spring源代码,将Spring AOP实现从deleration更改为proxy.invokesuper()
并提交一个拉请求。不过,我不确定这样一个突破性的变化会被接受。
操作步骤: 菜单栏: Refactor —> Replace Inheritance with Delegation...
问题内容: 我查看了Android中SensorManager的源代码,发现当您将侦听器的passs控制注册到时,就会发现。 我仅以此为例。我阅读了有关委托编程的Wikipedia文章,但仍不确定其目的。为什么要使用“委托”?它如何帮助控制程序?使用(或不使用)一个的缺点是什么?与听众一起使用最实用吗? 编辑:在第487行,有问题的方法在第1054行附近。 问题答案: 在GoF书中,委托并不完全是
问题内容: 我不理解javascript中的这种行为来继承,我一直都这样定义它: 但就我而言,这些行: 当我在Spaceship构造函数中执行console.log(this)时,可以看到 proto 属性设置为Spaceship而不是GameObject,如果删除它们,则将其设置为GameObject。 如果我使用: 我对此没有更多问题。之所以阻止我,是因为我有另一个具有add()方法的对象,并
本文向大家介绍C#中的委托是什么?事件是不是一种委托?相关面试题,主要包含被问及C#中的委托是什么?事件是不是一种委托?时的应答技巧和注意事项,需要的朋友参考一下 答: 委托是将一种方法作为参数代入到另一种方法。 是,事件是一种特殊的委托。 //比如:onclick事件中的参数就是一种方法。
问题内容: 偏重于继承而不是继承 是非常流行的短语。我读了几篇文章,最后每篇文章都说 当类之间存在纯IS-A关系时,请使用继承。 本文中的一个示例: 在 Apple 和 Fruit 之间存在明显的IS-A关系,即Apple IS-A Fruit,但作者也将其显示为Apple HAS-A Fruit(组成),以显示通过继承实现时的陷阱。 我在这里变得有些困惑,声明的含义是什么 当类之间存在纯IS-A
本文向大家介绍C#中的委托是什么?事件是不是一种委托?事件和委托的关系。相关面试题,主要包含被问及C#中的委托是什么?事件是不是一种委托?事件和委托的关系。时的应答技巧和注意事项,需要的朋友参考一下 委托可以把一个方法作为参数代入另一个方法。 委托可以理解为指向一个函数的指针。 委托和事件没有可比性,因为委托是类型,事件是对象,下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实