专栏首页JAVA高级架构Spring AOP 失效的真正元凶

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

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

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.

文章摘自:Spring官网

Java高级架构∣干货|交流

本文分享自微信公众号 - JAVA高级架构(gaojijiagou)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-03-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 面试重点:Java虚拟机常见问题详解

    一、Java引用的四种状态: 强引用: 用的最广。我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用。 如果一个对象具有强...

    Java高级架构
  • AOP 那点事儿 ( 续集 )

    在上篇中,我们从写死代码,到使用代理;从编程式 Spring AOP 到声明式 Spring AOP。一切都朝着简单实用主义的方向在发展。沿着 Spring A...

    Java高级架构
  • 为什么你要使用这么强大的分布式消息中间件——kafka

    这些场景都有一个共同点: 数据是由上游模块产生,上游模块,使用上游模块的数据计算、统计、分析,这个时候就可以使用消息系统,尤其是分布式消息系统!

    Java高级架构
  • Holiday -- hack the box

    Holiday is an insane box officially. It's really difficult to get the user permi...

    madneal
  • Tree - Information Theory

    This will be a series of post about Tree model and relevant ensemble method, inc...

    风雨中的小七
  • SAP UI5应用里类型为Edm.DateTime的日期控件设计原理

    Recently I was struggled with a customer incident and finally I realized that I ...

    Jerry Wang
  • Nibbles - Hack the box

    Target: 10.10.10.75(OS: Linux) Kali linux: 10.10.16.44

    madneal
  • windows平台下redis安装及配置文件介绍

    redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(...

    写代码的猿
  • Cronos -- hack the box

    3 ports is open, detect the detailed services:

    madneal
  • My Lead OPA test failure caused by Dialog close animation

    One colleague encountered My Lead OPA test faiulre on test case “Add Product tes...

    Jerry Wang

扫码关注云+社区

领取腾讯云代金券