Understanding Magic Dust in Java and the JVM (part 2)

4 minute read

The technical nitty gritty

In part one we examined how the behavior associated to annotations is injected into the application. In the next few sections we’ll see how the implementation of the behavior actually works.

Dynamic Proxies

Dynamic Proxies are a special kind of object. These object implement one or more interfaces, but their behaviour is defined not by a class but by an InvokatinHandler. Any call to any of the methods of the declared interfaces will be dispatched to the InvocationHandler. Dynamic Prox

For example, a very simple InvocationHandler would look like this:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TestInvocationHandler implements InvocationHandler {
    private final Object target;

    public TestInvocationHandler(Object target) {
        this.target = target;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.nanoTime();
        System.out.println("Starting profiling");
        try {
            return method.invoke(target, args);
        } finally {
            long endTime = System.nanoTime();
            System.out.println("Execution time: " + (endTime - startTime));

The code to actually create the proxy looks like this:

import java.lang.reflect.Proxy;

public class DynamicProxyTest implements Runnable{

    public static void main(String... args) {
        DynamicProxyTest object = new DynamicProxyTest();
        Runnable proxy = (Runnable)Proxy.newProxyInstance(
            new Class[]{Runnable.class},
            new TestInvocationHandler(object));
    public void run() {

Keeping in mind that Dynamic Proxies are special mechanisms buried deep in the JVM, here are some useful properties to keep in mind:

proxy.getClass(): class com.sun.proxy.$Proxy0
proxy instanceof Runnable: true
Runnable.class.isAssignableFrom(proxy.getClass()): true


  • JVM Dynamic Proxies can only proxy interfaces, so no enhancing of concrete or abstract classes
  • Any and all invocations must be handled by the method interceptor
  • Heavy use of reflection
  • All usual limits of JVM are enforced, such as the limitation on the number of interfaces that can be implemented

Bytecode Manipulation

Dynamic Proxies were only first introduced in Java version 1.3, and even then their performance in the early days was far from ideal. Before Dynamic Proxies became a truly viable option, other mechanisms were devised by the Java community to apply these kinds of behaviours (even before the advent of annotations in Java 5). The solution was to create Java classes on the fly, to provide the desired functionality. To achieve this, libraries were created to manipulate Java bytecode, which is the instruction set of the Java virtual machine. The most established byte code manipulation libraries are ASM and CGLIB. However newer and easier to use libraries have recently emerged, such as Javassist

Bytecode manipulation ranges from very low level using JVM opcodes to more higher level mechanisms such as CGLIB’s Enhancer. The Enhancer will generate a dynamic subclass, which enable dynamic interception. If you ever see a class that ends in $$EnhancerByCGLIB in your debugger, you’ll know that you’re working with a class that was “Enhanced” by CGLIB.

The following example enhances a class, by writing the output in HTML paragraph tags: <p> and </p>.

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.MethodInterceptor;

import java.util.function.Supplier;

public class EnhancerTest implements Supplier<String> {

    public static void main(String... args) {
        System.out.println(new EnhancerTest().get());

    public static Supplier<String> testInvocationInterceptor() {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback((MethodInterceptor) (obj,  method,  args, proxy) -> {
                    && method.getReturnType() == String.class) {
                return "<p>" + proxy.invokeSuper(obj, args) + "</p>";
            } else {
                return proxy.invokeSuper(obj, args);
        return (Supplier) enhancer.create();


    public String get() {
        return "Hello Test!";

The output will look like this:

Hello Test!
<p>Hello Test!</p>

So how does a framework like Spring implement functionality such as transaction propagation and security? In many case the secret behind the proxies is ThreadLocal!

In transaction management for example, before entering a method marked as @Transactional, the proxy code will check in a ThreadLocal variable to see if there’s a transaction in progress. If there is, it will be reused (reentrant transactions), otherwise a new one will be created and stored in ThreadLocal for access down the stack.

Security annotations work in a similar fashion, with the authentication code storing the authorization information in ThreadLocal. This information is it’s accessible by the proxy code wrapping methods annotated with @RolesAllowed.

While this is not a discussion about ThreadLocal it’s worth mentioning that as the name implies the information stored in such a variable is only available to code executing in the same thread. Special logic must take care of propagating this information to other threads in the JVM, or to a remote thread in case of RMI.

For more information on how ThreadLocal works, take a look at this post here: ThreadLocal, and how it holds apps together.

Final notes

Despite their bad rep in the early days, JDK dynamic proxies have performance that is within the realm of bytecode generation. Nowadays the decision between using JDK Dynamic Proxies or one of the code generation libraries is not clear cut. For example Spring will fallback to JDK Dynamic Proxies if no code generation library is available (and as long a you’re only injecting interfaces).
Regardless of the chosen method, just keep in mind that behind the covers, most moder web application are using plenty of proxies for a variety of critical functionality!

Thanks for reading, and if you have any questions or comments, feel free to drop a note in the comment section.