Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Same class invoke NOT effective in Spring AOP cglib [duplicate]

Tags:

java

aop

cglib

Suppose we have following class

@Service
class MyClass {

    public void testA() { 
        testB();
     }

    @Transactional
    public void testB() { ... }
}

Now, if we invoke myClass.testA(); in test, then @Transactional on testB will not take effect. The reason I think is following.

Cglib will create a proxy bean for MyClass, like this:

Class Cglib$MyClass extends MyClass {

    @Override
    public void testB() {
        // ...do transactional things
        super.testB();
    }
}

Now we invoke myClass.testA(), which will invoke MyClass.testB() instead of Cglib$MyClass.testB(). So @Transactional is not effective. (Am I right?)

I tried to add @Transactional for both methods (i.e. testA() and testB()). The proxy class should like this.

Class Cglib$MyClass extends MyClass {

    @Override
    public void testA() {
        // ...do transactional things
        super.testA();
    }

    @Override
    public void testB() {
        // ...do transactional things
        super.testB();
    }
}

In this case, although we successfully invoke Cglib$MyClass.testA(), it will still goes to MyClass.testB().

So my conclusion is, two methods in same class invoking each other will make aop annotation fail to take effect, unless we use AopContext.currentProxy().

Am I right on above guess? Thanks very much for advice!

like image 479
zchen Avatar asked Mar 21 '17 08:03

zchen


2 Answers

It is a well-known and documented (please search for the term "self-invocation") fact that Spring AOP, due to its proxy-based nature, does not and cannot capture internal method calls like this.someMethod(..).

So as you said, you either need to explicitly refer to the exposed proxy object or alternatively switch from Spring AOP to full AspectJ via load-time weaving.

like image 141
kriegaex Avatar answered Nov 14 '22 11:11

kriegaex


You almost have it right. The proxy looks something more like this:

class Cglib$MyClass extends MyClass {

  MyClass delegate;

  @Override
  public void testB() {
    // ...do transactional things
    delegate.testB();
  }
}

Any call is forwarded by Spring which is why your nested annotations are not activated.

Also, if a virtual method like testA was overridden, Spring could not avoid to invoke the overridden method.

like image 20
Rafael Winterhalter Avatar answered Nov 14 '22 10:11

Rafael Winterhalter