【发布时间】:2018-01-22 16:07:46
【问题描述】:
众所周知,在没有AspectJ的情况下,bean的方法的自调用在Spring中是行不通的。
例如,请参阅this question。
我认为这是因为 Spring 创建的代理使用 delagate 模式调用目标对象的方法。像这样:
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 bean(实际上是MyProxy 实例) 在MyClass 中调用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()绕过代理,因为在这种情况下this.myMethod(),从MyClass.myOtherMethod()调用(我们记得MyClass bean实际上是MyProxy实例),将调用覆盖的child的方法(MyProxy.myMethod())。
那么,我的主要问题是为什么不这样实现?
【问题讨论】:
-
我不是在问为什么 Java 动态代理 是以它们的方式实现的。我在问为什么 Spring AOP 代理 以它们的方式实现。据我所知,他们在代理模式 TARGET_CLASS 中使用 CGLIB 或 Javassist,而不是 Java 动态代理。是的,我的问题确实如此。多年以来,每次使用自注入我都会哭(这是容易出错的丑陋模式,LTW 也有自己的问题)。所以我真的想知道为什么 Spring 开发人员不使用
super来实现它(是有原因还是只是“它发生了”)。而且我敢肯定,许多 DEV 多年来一次又一次地问自己同样的问题 -
没有。默认情况下,Spring 在代理接口时使用 Java 动态代理。只有当你需要代理不实现接口的类时,你才需要CGLIB。或者,您可以将其切换为也将 CGLIB 用于接口。无论如何,两者都非常相似,因为它们都是动态代理并且不支持通过
this进行内部调用。顺便说一句,我怎么知道你使用的是哪种模式?您没有显示任何配置。我只能从你的代码中猜测,也许。 BTW2,你收到的近距离投票不是我的,我只是提到它。欢迎您提供我关于 AspectJ 的提示(那里没有代理)。 -
仅供参考,代理模式 TARGET_CLASS (CGLIB) 是 Spring Boot 2.0 中的默认模式。此外,有问题的代码故意不使用任何接口,因此无论如何它将在任何 Spring 版本中使用 CGLIB。 PS。冷静点,我不担心近距离投票。我知道这个问题并不完全适合 SO。我有点希望 Juergen Hoeller(也许是世界上唯一一个能给出有意义答案的人)真的回答它。 :-) 这只是一次尝试,但也许..
-
我确实给了你一个有意义的答案:如果你不喜欢 Spring AOP 的工作方式,请使用 AspectJ。没有动态代理(CGLIB 和 Java 都没有),不用担心。关于为什么很多年前有人可能以这种或那种方式做出设计决定的哲学讨论或猜测在 SO 上真的不合适。我的看法是:Java 动态代理存在于 JRE 中,CGLIB 也存在,所以 Springsource 只是使用它们。 P.S.:你没有标记问题spring-boot,那么我怎么知道它的默认值的相关性呢?我不使用Spring,我只是碰巧知道一些。
-
是的,你确实给出了答案,但在一个完全不同的问题上(我根本没有问)。 :-) 问题是为什么CGLIB 代理使用Delegation pattern 而不是Inheritance 实现(这应该解决
this问题)。这不是哲学讨论!这是针对弹簧专家的真正精确的问题。而且在 cmets 中的讨论也对 SO 不利,所以让我们关闭它。如果你真的想讨论 - 我们去聊天吧。谢谢。
标签: java spring proxy spring-aop