Home » Software Development Resources » Using CGLIB with proxy-target-class=”true”

Using CGLIB with proxy-target-class=”true”

In this post, we are going to take a technical deep dive into the world of Java, AOP, and Bytecode in order to understand an undesirable side effect of using CGLIB proxies.

I wrote this article out of pain and a misunderstanding of the intricacies of AOP. What started out as a run of the mill Null Pointer Exception(NPE) turned into a deep study of the mechanisms that offer such great abilities. You should read this article if you want to see under the hood of how Java deals with different aspects of well…. Aspect Oriented Programming!

If you are reading this I assume some basic understanding of the following:

  • Aspect Orient Programing (AOP) is a modular approach that allows the separation of cross-cutting concerns. This is all still JAVA code but we can edit all functional areas of code at once! Example: Log every HTTP packet sent and received, without changing our client code.
  • CGLIB is a Bytecode generation library for AOP used by projects such as Hibernate, Spring, and Google Guice.
  • Proxy Mechanisms is a method for creating proxies for a given target object.
  • proxy-target-class is an attribute of the AOP config that, if set to “true” forces the proxying to use CGLIB proxies instead of Java Proxies
  • Null Pointer Exception (NPE) is the bane of any software engineer that occurs when a reference to an object contains a NULL value.

I use AOP often – recently for a dynamic datasource router for a project. We encountered several tricky situations, such as creating an annotation using “@Around”. In order to get the correct point cut, we had to set the proxy-target-class=”true” attribute value.

For the most part, this had no side effects and enabled our dual routing sources! However, a single NPE popped up that was a rabbit hole of AOP. Checkout the code below to get an understanding of the situation:

@Autowired
private CommonDAORepository commonDAORepository;
private int incomeHash = 0;
private void refreshIncomeHash(){
  this.incomesHash = this.commonDAORepository.getAllIncomes().hashCode(); 
}
@Override
public int getIncomeHash() {
  if(this.incomeHash == 0){
    this.refreshIncomeHash();
  }
  return incomeHash;
}

During runtime, commonDAORepository and ALL OTHER private/protected members are NULL! But wait you say! “This is not suppose to happen! Everything is Autowired!”

Despite getIncomeHash being Public, since there is a Private call to refreshIncomeHash, we will be placed into a proxy shell object. At this point, all members and auto injected components will be NULL, specifically commonDAORepository in this case. The source of the NPE.

Screen shot of the IDE view showing you were you THOUGHT your object was

 

The fix is to convert refreshIncomeHash from PRIVATE to PUBLIC

 

Another screen shot of the IDE showing your where your object ACTUALLY is

You are in a very specific situation if using these methods or approach.
It’s not that bad. There area reasons why this has happened, some of them are listed below:

  • The CGLIB proxy will not proxy any public function!
    • I’m sure the reason is buried deep in here: http://cglib.sourceforge.net/apidocs/index.html
  • CGLIB is NOT an intrinsic part of Spring. Not every bean has to be managed by Spring!
  • CGLIB actually manipulates bytecode at runtime to achieve it’s goals.
  • CGLIB generates a subclass to override non-final/protected/private of the proxied class.
    • Note that this does NOT include PUBLIC, this is very important!

If you tried debugging the NPE you would find something like this inside of your NULLd class.

— CGLIB$CALLBACK_0 -> Advised -> Proxy Object
— CGLIB$CALLBACK_1 -> Target -> REAL OBJECT before wrapping

This subclassed Proxy object does NOT call SUPER(). It simply delegates with this shell of an object.

Wrapping up, AOP programming is clearly very useful. However it does some with some sharp edges when you want to do more advanced/detailed things! I doubt this issue would have ever come up if it was not for wanting dynamic routing data sources via annotations!

Scroll to Top