Applying “Java Aspects” at Load Time: Java Instrumentation API
Load time Weaving of Java aspects using Java Agent and AspectJ Weaver
I bet you are thinking, man! this image does not go with the title. Amusingly, that is true but just for the ones who are not able to picture the working of Java Aspects at the application load time. Fortunately, that bodes well for me as that is precisely what I am going to decipher through this article.
So let’s begin with having another look at the image:
The above image demonstrates the custom weaving multiple cotton yarns using a tool that facilitates the process by bringing the yarns together.
So how exactly is this analogous to our motive:
In this article I will demonstrate the custom weaving of multiple classes in a Java application (in our case Spring Boot), where at least one class is a Java Aspect (in our case AspectJ), using a tool called Java Agent that facilitates the process by bringing the classes together, allowing to manipulate the bytecode of a compiled Java class.
What is a Java Aspect?
It is really another world when it comes to Aspect Oriented Programming (AOP). Although it is important to properly understand AOP to the core, it is out of scope for this article. But I’ll give the crux of the concept.
AOP is a programming standard that aims at abstracting the boiler-plate code (common code snippet that is used repeatedly) from a Java application by providing a declarative architecture through which developers can abstract the boiler-plate code and provide it where ever they are needed in their application.
An Aspect is nothing but this abstracted boiler-plate code, written as a method in a class (Aspect class). Intuitively, a Java aspect is an aspect class written in Java.
OK! Now what is Java Instrumentation API?
I know that there are a ton of great articles out there describing all about this API of Java. I recently came across a comprehensive one by Ruby Valappil, where she demonstrated a fully functional example.
What I’ll do here is take the crux of the concept and analogize it to match it our motive.
Java Instrumentation is the ability of the JVM to alter the bytecode of an already compiled Java class.
This API is exposed by the underlying JVM of the application.
So now we know that we need to apply our Java Aspects to some of the places in our Java application and we also know that Java Instrumentation API can alter the bytecode of a compiled Java class.
Ok, I’ll admit, nothing makes sense yet.
Connecting the dots
The obvious question now is:
“How do I make use of the above 2 concepts: To abstract boiler-plate code in my Java application and apply it wherever required at load time?”
Let’s take it one at a time.
How are Aspects applied to a component?
In Java, aspects can only be applied to methods. So when we say “applying aspect to a method” we mean that the aspect method’s logic should get incorporated in the called method’s logic since it is originally an abstracted part of the called method.
So JVM wraps the called method’s logic with the aspect method’s logic by invoking the aspect method before or after the called method, whenever it is invoked. Thereby, keeping the integrity intact.
Why do I need to apply it at “Load Time” in the first place?
Yes, it is extremely important to see why exactly at load time. For that matter, one might also wonder how many ways are there to apply a Java Aspect to my application. If you are having the same questions, congratulations !
Basically, there are 2 ways in which aspects are applied to a Java application:
- At run-time: Where the abstracted code snippets (aspects) are called at the run time as and when they are required by the application components. At run time AOP works by creating class proxies, read more about it here.
- At load-time: This refers to the stage in the lifecycle of a Java application where all the compiled .class files are being loaded into the JVM by the Java ClassLoader. Here, we attempt to apply the aspect right into the bytecode of the target class file.
Both the approaches sound good but the caveat that enables one to choose one over the other is the nature of the application component onto which this aspect has to be applied.
If you understand how Java works, you could sense that at run time if the method, onto which the aspect has to be applied, is not accessible, the aspect cannot be applied. This means that at run time only public methods can have an aspect applied to them but not the private ones.
On the other hand, it violates Java’s core principles like abstraction and encapsulation, to make a method public just because you want to apply an aspect to it. Naturally, there has to be a way to apply aspects onto private methods. Mind you, private methods cannot be called from outside their respective classes.
Enter Load Time Weaving.
Load time weaving (LTW) refers to the weaving of the aspect’s bytecode into the bytecode of another class where this aspect is required, without disturbing the actual Java source code of the application.
How does Java instrumentation helps here?
This is pretty clear by now. We know that at load time we need to alter the bytecode of the class where the aspect has to be applied.
Java Instrumentation API, just like any other API, is a set of interfaces which needs implementation. Java Agents are the concrete implementation of this API. An example is the spring-instrument’s InstrumentationSavingAgent.
This Java Agent class, provided by the implementation of this API, helps us intercept a class’s bytecode and alter it to weave the aspect’s bytecode into it.
Action Please !
Easy said than done right?
So I got to know about this recently when I was building a demo project for one of my previous tutorial series on using Google OAuth 2.0 to scrape mail servers:
Use case
In a web based application, more often that not there are a lot of network calls made to a third party API. Our use case is to automatically retry, strictly once, any API network call made to the third party API in case there is a intermittent network issue.
Boiler-plate code: The retry mechanism for every API network call.
LTW scenario: All the API network calls are made through a private method in the application.
Step 1: Create the required Aspect
We will use AspectJ library to create an aspect class (Details about AspectJ are out of scope for this article):
We use “@Aspect” annotation from the AspectJ library, to mark a Java class as an aspect.
By using “@Around” annotation we specify the advice of the aspect. Pointcut expression is passed as a parameter to this annotation. Basically what we are doing is providing a way for the JVM to identify where exactly to apply this particular aspect in our Java application.
Read more about AspectJ here.
This aspect class abstracts out the retrying mechanism in the retry method.
Step 2: Create a weaver
Once we have the aspect created, we need the necessary configurations and tools to weave it to the selected places in our application.
The tool we need for this is the AspectJ weaver. This jar is capable of understanding which classes are to be woven. The default class used as the weaver from the AspectJ weaver jar is “DefaultContextLoadTimeWeaver”.
The way we provide this information to the weaver is by providing the following configuration:
It is imperative to keep the name as aop.xml for the above file and place it under the project folder src/main/resources/META-INF/
Step 3: Enable Load Time Weaving for your application
In spring boot applications, we just need to add an annotation on the main class like this:
Notice the “@EnableLoadTimeWeaving” annotation. This enables the spring boot application to look for the aop.xml in the META-INF folder under src/main/resource project folder.
Step 4: Bring Java Agent to the picture
As already discussed, Java agent is the real entity that does the weaving part. We need to provide this Java agent (another Java class) to our application so that it is available even before the main method of our application is invoked.
We do this by launching our application with a particular JVM argument:
java -javaagent:<path_to_java_agent> cp <path_to_our_application_jar> <main_class_name>
As we discussed earlier, this agent class comes with the Java Instrument API. So we need to get an implementation of the API. In our case, we will use the spring-instrument jar. Path to this spring-instrument jar will go as the value of “-javaagent:” argument above.
Step 5: Make sure aspectjweaver.jar is in classpath
Along with the spring-instrument jar, we also need to provide aspectjweaver.jar, like this:
java -javaagent:<path_to_java_agent> -javaagent:<path_to_aspectj-weaver_jar> cp <path_to_our_application_jar> <main_class_name>
Conclusion
So this is how we weave Java aspects into our application classes using the Java Instrumentation API at load time.
Please note that Spring-AOP module, of the Spring Framework, alone is only capable of proxy-based aspect oriented programming. LTW is a full blown AspectJ support which does not require proxying of classes.