您的位置:首页 > 编程语言 > Java开发

译-Spring-理解AOP代理

2017-06-29 15:59 267 查看
更多请移步: 我的博客

引入

之前写过一篇关于Spring代理流程的博客,当时没有深入思考,最近碰到一个有趣的事情,类内部调用带有spring注解,但注解不生效的问题,举例说明:

public class SimplePojo implements Pojo {

public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}

@Transaction
public void bar() {
// some logic...
}
}


外部一个类直接调用SimplePojo.bar()事务是有效的,但如果调用SimplePojo.foo(),那么bar()方法上的事务不会生效。

查了一下spring文档,其中特别对AOP代理做了讲解,现翻译记录。

对照译文

原文链接:https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

Spring AOP is proxy-based. It is vitally important that you grasp the semantics of what that last statement actually means before you write your own aspects or use any of the Spring AOP-based aspects supplied with the Spring Framework.

Srping AOP基于proxy-based。在我们基于Spring框架自定义切面或者使用Spring基于AOP-based提供的切面之前,理解proxy-based的含义很重要。

Consider first the scenario where you have a plain-vanilla, un-proxied, nothing-special-about-it, straight object reference, as illustrated by the following code snippet.

看下面一个最简单的调用片段。

public class SimplePojo implements Pojo {

public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}

public void bar() {
// some logic...
}
}


If you invoke a method on an object reference, the method is invoked directly on that object reference, as can be seen below.

如果你使用对象的引用调用其一个方法,这个方法是被直接调用的,看下图。



public class Main {

public static void main(String[] args) {

Pojo pojo = new SimplePojo();

// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}


Things change slightly when the reference that client code has is a proxy. Consider the following diagram and code snippet.

当客户端代码的引用是代理时,会略有改变。请看下图和代码片段。



public class Main {

public static void main(String[] args) {

ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());

Pojo pojo = (Pojo) factory.getProxy();

// this is a method call on the proxy!
pojo.foo();
}
}


The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

着重看下Main类中main方法中对代理的引用。这意味着对该对象引用的方法调用实际调用的是代理,因此代理能够将调用委托给与该方法调用相关的所有拦截器(advice)。然而,一旦调用最终到达目标对象,例子中的SimplePojo引用,那么其任何自我方法的调用,例如this.bar()或者this.foo(),都将是直接调用,而不是调用的代理。这点特别重要。这意味着自我调用不会被其相关联advice不会被执行。

Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen. For sure, this does entail some work on your part, but it is the best, least-invasive approach. The next approach is absolutely horrendous, and I am almost reticent to point it out precisely because it is so horrendous. You can (choke!) totally tie the logic within your class to Spring AOP by doing this:

那么我们要做些什么呢?最好的方式是重构你自己的代码使得不会发生自我调用。当然,这需要一些工作量,但这是最好并且低侵入的方式。另外一种方式绝对是可怕的。你可以将你的逻辑与Spring AOP完全耦合,比如:

public class SimplePojo implements Pojo {

public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}

public void bar() {
// some logic...
}
}


This totally couples your code to Spring AOP, and it makes the class itself aware of the fact that it is being used in an AOP context, which flies in the face of AOP. It also requires some additional configuration when the proxy is being created:

这完全将你的代码与Spring AOP结合在一起,它使得这个类本身就意识到它被用在一个面向AOP的AOP上下文中。它也使用时也需要加入一些配置:

public class Main {

public static void main(String[] args) {

ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.adddInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
factory.setExposeProxy(true);

Pojo pojo = (Pojo) factory.getProxy();

// this is a method call on the proxy!
pojo.foo();
}
}


Finally, it must be noted that AspectJ does not have this self-invocation issue because it is not a proxy-based AOP framework.

最后,必须注意的是,AspectJ没有这种自我调用问题,因为它不是基于proxy-based的AOP框架。

总结

回顾了下上一篇关于AOP源码阅读的博客,其实代码中已经给出答案。只是当时并没想这么多。

代理类实际上是对目标类做了聚合封装。这也就解释了为什么自我调用时advisor不会被执行。

//AbstractAutoProxyCreator
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

...

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}

proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: