Spring 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.

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
   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

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
      factory.addAdvice(new RetryAdvice());

      Pojo pojo = (Pojo) factory.getProxy();      
      // this is a method call on the proxy!

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.

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:

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:

public class Main {  
   public static void main(String[] args) {
      ProxyFactory factory = new ProxyFactory
(new SimplePojo());
      factory.addAdvice(new RetryAdvice());

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

      // this is a method call on the proxy!

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

Programmatic creation of @AspectJ Proxies

In addition to declaring aspects in your configuration using either <aop:config> or <aop:aspectj-autoproxy>, it is also possible programmatically to create proxies that advise target objects. For the full details of Spring's AOP API, see the next chapter. Here we want to focus on the ability to automatically create proxies using @AspectJ aspects.

The class org.springframework.aop.aspectj.annotation.AspectJProxyFactory can be used to create a proxy for a target object that is advised by one or more @AspectJ aspects. Basic usage for this class is very simple, as illustrated below. See the Javadocs for full information.

// 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();

Using AspectJ with Spring applications

Everything we've covered so far in this chapter is pure Spring AOP. In this section, we're going to look at how you can use the AspectJ compiler/weaver instead of, or in addition to, Spring AOP if your needs go beyond the facilities offered by Spring AOP alone.

Spring ships with a small AspectJ aspect library, which is available standalone in your distribution as spring-aspects.jar; you'll need to add this to your classpath in order to use the aspects in it. Section 7.8.1, “Using AspectJ to dependency inject domain objects with Spring” and Section 7.8.2, “Other Spring aspects for AspectJ” discuss the content of this library and how you can use it. Section 7.8.3, “Configuring AspectJ aspects using Spring IoC” discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally, Section 7.8.4, “Load-time weaving with AspectJ in the Spring Framework” provides an introduction to load-time weaving for Spring applications using AspectJ.



原文发布于微信公众号 - JAVA高级架构(gaojijiagou)





0 条评论
登录 后参与评论




以前有意找这方面的资料,今天突然发现在系统中就有 linux系统用man hier solaris用man  filesystem 其结果如下     ...


domaincontext load 回调

This post is not specific to RIA Services but I thought I'd add it to the title ...


Spring boot with Email

本文节选自《Netkiller Java 手札》 地址: http://www.netkiller.cn/java/ 9.12. Spring boot wit...



  dd if=FreeBSD-8.2-RELEASE-i386-memstick.img of=/dev/sdb bs=10240 conv=sync 二,安...


Spring boot with Velocity template

本文节选自《Netkiller Java 手札》 地址: http://www.netkiller.cn/java/index.html 9.13. Sprin...



但是走到了 SplitNCigarReads 才发现,这个命令当初学的太久了,忘记各个参数啥意思了,就想搜索看看如何转换。


Hadoop基础教程-第9章 HA高可用(9.3 HDFS 高可用运行)(草稿)

第9章 HA高可用 9.3 HDFS 高可用运行 9.3.1 HA节点规划 节点 IP Zookeeper NameNode JournalNode Da...


Zend Framework自动加载、简单路由配置(Bootstrap.php)

<?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { /* * 自动加...


转载:Package by feature, not layer

The first question in building an application is "How do I divide it up into pac...


Caused by: java.net.ConnectException: Connection refused: master/

1:启动Spark Shell,spark-shell是Spark自带的交互式Shell程序,方便用户进行交互式编程,用户可以在该命令行下用scala编写spa...