0

So I have been trying to wrap my head around cglib proxy . So from what i understand it should roughly inherit the target class we are proxying and essentially inject another instance of the target which is actually invoked inside it.

So , for example I have this as the class -

@Component
public class A {
  private String silly ="a";

  public final void processMyStuff() {
    System.out.println(this.silly + "b");
  }
}

The aspect is declared by a class like -

@Component
@Aspect
public class ProfileAspect {
  private static Logger log = LoggerFactory.getLogger("ProfileLogger");

  @Around("publicMethods()")
  public Object profileLogging(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    Long startTime = System.currentTimeMillis();
    Object proceed = proceedingJoinPoint.proceed();
    Long endTime = System.currentTimeMillis();
    log.info("responseTime: {} ms for class {} in method {}",
        (endTime - startTime),
        proceedingJoinPoint.getTarget().getClass().getName(),
        proceedingJoinPoint.getSignature().getName());
    return proceed;
  }

  @Pointcut("execution(public * Service.A.*(..))")
  private void publicMethods() { }
}

So , as expected when I do the following -

//where "a" is a spring managed instance which we have simply injeted at the point of invocation 
a.processMyStuff();

The output is -

nullb

So my main query here is this -

If I do write similar code in plain java , the parent classes instance variable is referred when a final method is invoked in a similar fashion via the instance of the child class. So the output there would be "ab".

With cglib from what i understood final methods should be unaffected, so in this case why is the proxy's value of the instance variable "silly" (null) is printed ?

Edit 2:

So just to share the normal java code that should in theory map to what a proxy does -

public class A {
  private String silly ="a";

  public final void processMyStuff() {
    System.out.println(this.silly + "b");
  }
}

inherited class [ equivalent of the proxy class] -

public class B extends A {
  private A a;

  public B (A a){
    this.a = a;
  }
}

invocation -

B b = new B(new A());
b.processMyStuff();

outupt - ab

So, the comparative output is "ab" . So I am trying to understand what I am understanding wrong here.

Edit 3: So as per my current understanding of cglib could in theory delegate calls to the superclass but it seems like it delegates them to another instance.

This is a me trying to create a proxy object using springs constructs directly -

class AdviceInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // run code before method execution
        System.out.print("Before target method execution " + invocation.getMethod().getName());
        // target method execution
        Object retVal = invocation.proceed();
        System.out.println("After target method execution " + invocation.getMethod().getName());
        return retVal;
    }
}

Target class -

public class Mutant {
    public String name="aa";

    /**
     * Enhancer requires the no-arg constructor to construct the proxy instance.
     * IllegalArgumentException is thrown if Enhancer can't find the no-arg
     * constructor during proxy creation time.
     */
    public Mutant() {
        System.out.println("in consutructor" + this.name+ this.getClass());
    }

    public String getName() {
        System.out.println("Mutant get name.."+ this.getClass().toString());
        return name;
    }

    public  final void eat() {
        System.out.println("Mutant eat.."+name+ this.getClass());
    }

    public  void eatPotato() {
        System.out.println("Mutant eatpotato.."+name+ this.getClass());
    }
}
Mutant target = new Mutant();

ProxyFactory pf = new ProxyFactory();
// Add MethodInterceptor as advice
pf.addAdvice(new AdviceInterceptor());
// set the target class you want to proxy
pf.setTarget(target);
// Get the proxy
Mutant proxy = (Mutant) pf.getProxy();
// Call target class print function via proxy
proxy.eatPotato();
proxy.eat();

Output -

Before target method execution eatPotatoMutant eatpotato..aaclass com.ie.naukri.feature.companyservice.Mutant
After target method execution eatPotato
Mutant eat..nullclass com.ie.naukri.feature.companyservice.Mutant$$EnhancerBySpringCGLIB$$46a1e333

So the invocation of the final method shows the instance variable as null. Stepping through the code, I found during the instantiation of the proxy object, spring seems to call -

SpringObjenesis#newInstance

This is used to create the proxy instance, simply using this to create a new instance of a random class also seems to set the instance variables to null!

Edit 4: So I just tried stepping through the code of my actual service which was using aspects with cglib . I put the debuggers in the classes I mentioned above and voila, spring went there to create its proxy instances!

Edit 5: Narrower question - cglib proxy and null instance variable internals

kriegaex
  • 63,017
  • 15
  • 111
  • 202
silver-soul
  • 87
  • 1
  • 8
  • When `A a = new A()` is used `a` is not a spring container managed bean and no corresponding proxying etc. Considering that , `nullb` shouldn't get printed , unless the actual code is different from the one shared – R.G Jul 12 '22 at 13:38
  • Updated, my bad. The instance a is managed by spring . pasted the wrong sample there . – silver-soul Jul 12 '22 at 15:33
  • Do go through the SO [Q&A](https://stackoverflow.com/a/50623367/4214241) that answers your question. – R.G Jul 12 '22 at 16:50
  • Instance variables are not inherited . – R.G Jul 12 '22 at 16:52
  • Had gone through that answer before, and it did clarify the concept . But what I am having trouble is understanding why does the output of the simple java code I have shared in my edit differ from the proxy pattern by cglib then . – silver-soul Jul 12 '22 at 18:59
  • The edit 2 example is not equivalent to proxy class. Any particular reason for the constructor for `class B`? – R.G Jul 13 '22 at 02:56
  • No, I have only included the instance variable to denote that if a non-final method was there , the B class would just call the method of the instance variable of A . What would be the logical equivalent in normal java code then ? – silver-soul Jul 13 '22 at 06:17
  • Do read through this SO [Q&A](https://stackoverflow.com/questions/41478307/whats-the-difference-between-spring-cglib-and-cglib) and then [this blog](http://hauchee.blogspot.com/2015/09/cglib-enhancer-good-substitution-for-java-dynamic-proxy.html). – R.G Jul 13 '22 at 06:54
  • This was a very interesting read. But this raised some more questions! When i stepped through and debugged their code I could see that the value of instance variable was not being set to null even for final method. Also in the code samples they just call the super class methods in instead of calling a proxy objects methods . This mentions why I said spring delegates cglib proxy to an actual instance - https://stackoverflow.com/a/39208811/6056707 Also the diagram shown here - https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html Adding more details in edit! – silver-soul Jul 14 '22 at 19:41
  • This is already a long question and I am not sure what exactly you need clarity on. I would suggest you close this question and ask a specific question. There are many helpful souls here , who could help you with AspectJ and Spring internals. – R.G Jul 15 '22 at 02:22
  • Sure, i think i have my answer now . Will form a better worded question and validate my theory there.thanks for the help! – silver-soul Jul 15 '22 at 05:16
  • Even though this question is more detailed and was written before the second one, I chose to close this one as a duplicate and answer the other one, because this question is also confusingly detailed with its many edits, without adding much to learn for readers. The answer is actually simple, so it is better to focus on the new question and my answer there. – kriegaex Jul 20 '22 at 20:03

0 Answers0