简体   繁体   中英

Spring JDK Dynamic Proxy and CGLIB - implementation details

Where this question stands, relative to other popular questions on SO

My question is related to "self-invocation problems inside proxies", but it's not my question. (I already found some solutions to that problem, as described, for example, here ). I also found the basic explanation for why this happens, here

Introduction to the current situation

To start from the beginning, I first encountered the problem of "self-invocation" at work. We are using Spring Boot and we have some services that need both to Audit some data (using AspectJ), and to PreAuthorize that method. The method itself had to return something, but audit other things. So now our services look something like:

class Service {
    Service self;

    public Service() {
       // I don't like this because it makes my Service to be aware of the fact that it's a proxy. I just wanted my class to be a pojo with annotations
       self = AopContext.currentProxy();
    }

    @Audit
    public CustomClassContainingTheValuesToAudit getValuesFromServer() {
       CustomClassContainingTheValuesToAudit c;
       try {
           Object answer = self.doGetValuesFromServer();
           c = ... ; // create what to audit based on the answer
       } catch (...) {
          // log the fact that you didn't have enough privileges to run doGetValuesFromServer()
          // create c based on the exception I got
          c = ...;
       }
       return c; // object to be audited
    }

   @PreAuthorize(.....)
   public Object doGetValuesFromServer() {
       .........
   }
} 

What I want to achieve

My main concern it to get AspectJ + PreAuthorize work together, as described above, but without making my class aware of the fact that it's being proxied. So I wanted to understand exactly what my options in terms of types of proxies are. I found out that Spring comes with two flavors of proxies: JDK Dynamic Proxies and CGLIB proxies. It turned out that we are using CGLIB proxies, we configured Spring to use CGLIB (anyway, right now the Service is just a plain class, doesn't implement an interface). For a few hours, I read in the Spring documentation on AOP + whatever else I found on the internet, in order to understand how exactly my classes look like after being proxied. I thought that the only option to achievewhat I want is by using some other (third?) implementation of proxy

My question(s) about this.

1) Based on my description about how the code looks like now, what would you do to avoid taking the bean from the context? I don't want to break each service in two classes, in order to avoid self-invocation problems. I want a way to make aspectJ and PreAuthorize work as described. 2) I don't understand the reason why CGLIB implements proxies the way it does. So let's say I have my class Service

class Service {
    public void methodA(){
        // do methodA stuff
        // call method b
        methodB();
    }
    public void methodB(){
        // do methodB stuff
    }
}

Then GCLIB will generate a class like:

class Service$CGLIB extends Service {
// it's only extending my class to make it compatible with the variable where i'm storing/autowiring the proxy
   Service privateField; // but it's actually using a decorator pattern, just like JDK Dynamic Proxies do
   public void methodA() {
      // do any magic before
      privateField.a();
      // any magic after the call
   }

   public void methodB() {
       // magic
       privateField.b();
       // some more magic
   }
}

Why doesn't CGLIB just call super.a() and super.b() ? Why does it need to decorate instead of delegate? I think what I needed is for the proxy to delegate to super. I it were to do so, I didn't have any problems with self-invocation, because of polymophism. Is there a implementation of proxy that does what I expected CGLIB to do?

The behavior of the Cglib-proxies has nothing to do with the way of how cglib works but with how cglib is used by Spring. Cglib is capable of either delegating or subclassing a call. Have a look at Spring's DynamicAdvisedInterceptor which implements this delegation. Using the MethodProxy , it could instead perform a super method call.

Spring defines delegation rather than subclassing in order to minimize the difference between using Cglib or Java proxies. This is simply a choice made.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM