The latest expert opinions, articles, and guides for the Java professional.

How my new friend Byte Buddy enables annotation-driven Java runtime code generation


Last week, we looked into Java’s strong and static type system. We stipulated that such types make our code more expressive, but at the same time limit third-party frameworks in offering POJO-centric APIs.

We worked out that Java reflection is a great way of interacting with user code, but it comes at the price of losing type-safety. In order to retain type-safety, perhaps it’s better to use code generation for the creation of subclasses of a given user class at runtime?  For these classes, we override any method to implement our framework logic without requiring a user to depend on framework types.

I suggest you read the first part of this article if you haven’t read it already. I hope it helps you to get a feeling for the kind of problem code generation wants to solve and when you should rather use reflection.

Are we there yet?

We all know how to define a Java class (I hope), so there is nothing new I can teach you here. However, we normally express these class definitions in some JVM language before asking a compiler to translate our narratives into bytecode.

Java bytecode is a binary format but, other than that, the Java programming language and its bytecode are fairly similar. This is no surprise since the Java language sort of drives the evolution of the bytecode instruction set. If you want to know more about bytecode, there is a great report about Mastering JVM Bytecode available here on RebelLabs that will teach you all the tricks.

While directly working with Java bytecode gives you full control over the generated classes, it is also quite cumbersome to deal with. Bytecode is served to you without certain conveniences like auto-boxing or high-level control flows. Also, writing bytecode requires you to do a lot of boring stuff such as counting operand stack sizes and computing stack map frames. Wouldn’t it be great if you could skip all this in order to focus on writing your actual runtime code?

Of course it would. And for this reason, even the Java class library comes with limited support for defining runtime classes. These classes are known as JDK proxies and allow the runtime definition of a class which implements any set of interfaces.

An invocation of any interface method is then forwarded to an invocation handler which you are to implement and provide. When discussing JDK proxies, the emphasis lies on the implementation of interfaces. You cannot extend any class with these built-in Java proxies.

For our security framework from the last blogpost, this would mean that we could only secure interface methods and not just methods of any type. That’s a rather lame restriction. For this exact reason, cglib was released as an independent library shortly after the introduction of the JDK proxies.

Cglib works quite similar to the JDK proxies but is able to proxy any method by creating dynamic subclasses instead of only implementing interfaces. Cglib is still a popular choice and frameworks such as Spring rely on it for some of their functionality.

However, despite its popularity, cglib is no longer under active development and in the last years even bug fixes were only released occasionally. This is in particularly problematic since cglib consequently does not support new bytecode features such as Java 8 default methods.

Due to these unresolved issues with cglib, over the years an increasing number of projects decided to move away from the library. And at least after the Hibernate project made javassist its first choice, this Java assistant library became an obvious alternative for the mainstream.

To begin with, javassist comes with a proxy library that mainly imitates the functionality of cglib. Beyond that, javassist unfolds its full potency by offering a runtime Java compiler which allows the definition or redefinition of about any Java class by simply providing strings of Java code.

While it sounds amazing, writing code inside a String can quickly get out of hands, especially if you need to mix a compiled functionality with dynamic one. The code on the image below is a real-world example of what you can get into with javassist.

Moreover, while Java source code is a great and intuitive way of expressing a Java class; unfortunately, as of today, javassist is still a one-man project even though Red Hat started to sponsor the library. And with the steady advancement of the Java language, the javassist compiler increasingly lags behind the one of the JDK.

As a result, you still need to think bytecode even when writing javassist-flavoured Java code. As of this writing, you are still required to,  for example, perform explicit value boxings of primitive types. And guess what? With the introduction of new language features in Java 8, the gap between both compilers just became even bigger.

A little less complaining, please?

Now, it is easy to point fingers at the shortcomings of other people’s libraries, but how could we improve the situation? Both cglib and javassist were created early in this millennium and their APIs were built around the language features that Java had to offer back in these days.

One of the more significant innovations introduced after the inception of these libraries are annotations. It’s somewhat ironic that code generation is mainly used to implement annotation-based APIs while no code generation library is itself built with one. For this reason, I took on the challenge and wrote another library which walks down this path.

The library is called Byte Buddy and it uses annotations and a domain specific language for achieving its ambitions. Doing so, a runtime class can be created like in this code snippet:

new ByteBuddy()

If you can approximately grasp the meaning of this code, the library turned out as I hoped. One remaining mysterious bit might be the intercept method which takes a method delegation as its argument. When receiving such a method delegation, Byte Buddy will forward invocations of methods that match the given naming constraint to a static method in the given class. This target class and method could for example be implemented like this:

class ToStringInterception {
  public static String intercept(@Origin Class<?> type) {
    return type.getSimpleName();

With the interceptor above, any invocation of the toString() method on an instance of the dynamic class would result in a delegation to the sole static method. By using the @Origin annotation, the method receives a class reference as its only argument. Consequently, at the runtime the return value of a toString() invocation would be the intercepted class’s simple name.

This is only a scratch on the surface of Byte Buddy’s API but we won’t go into any library specifics here. But if you care, check out the official documentation on Byte Buddy’s website which reveals all the magic.

Regardless, in order to demonstrate that applied code is suited for every-day programming, let us use Byte Buddy to implement the SecurityLibrary that we mentioned in the first part of this article. As we will see, an immediate implementation that simply stores the logged-in user in a static field does not require more than only a few lines of code:

class ByteBuddySecurityLibrary implements SecurityLibrary {

  public static String currentUser = “admin”;

  public  Class<? extends T> secure(Class type) {
    return new ByteBuddy()
      .load(type.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)

  public static Object intercept(@SuperCall Callable<?> superMethod,
                                 @Origin Method method) throws Exception {
    if (!method.getAnnotation(Secured.class).requiredUser().equals(currentUser)) {
      throw new IllegalStateException(method + " requires appropriate login”);

Similar to the first example, we use Byte Buddy to intercept methods in order to delegate their invocation to a framework method. This time, we intercept methods that are annotated by the Secured annotation.

For these methods, we compare the annotation value to the currently logged-in user and throw an exception if this user does not equal the user that is required by the annotation. Otherwise, we invoke the original method.

In Java, it is not normally possible to invoke a super method from outside the instance on which the super method is invoked. To overcome this limitation, Byte Buddy automatically creates a proxy class which is defined similarly to a non-static inner class of the Java programming language. Doing so, a super method can be invoked as a Callable even from outside the intercepted method.

Hurray! We just defined a class at runtime and did it without using a compiler. Champagne! Let us call out a week of celebration and meet here again next week when we compare the performance of different code generation libraries. Cheers!

Write back at me or ask any questions about Byte Buddy, the best size of towel for the beach or answers about the origins of the universe in the comments below, or find me on Twitter @rafaelonjava.


Update! The final part of the series: Testing the performance of 4 Java runtime code generators: cglib, javassist, JDK proxy & Byte Buddy is published.

Responses (11)

  1. Avatar  

    Brian Oxley

    July 9, 2014 @ 10:42 pm

    What is @Context? I didn’t find it in bytebuddy from Maven central (0.2.1).

  2. Avatar  

    Rafael Winterhalter

    July 10, 2014 @ 8:12 am

    I renamed the @Context annotation to @Origin before the release, but apparently the “beta names” are still stuck in my head. Thanks for pointing this out!

  3. Avatar  


    July 10, 2014 @ 9:22 pm

    Have a look at being there for a seat or so.

  4. Avatar  


    July 10, 2014 @ 9:22 pm

    Year or so …

  5. Avatar  


    September 30, 2014 @ 3:44 am

    Don’t think the example above compiles:

    public Class secure(Class type) {
    return new ByteBuddy()
    .load(type.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)

    the “make()” reports “The method make() is undefined for the type ByteBuddy.MethodAnnotationTarget” in my IDE.

  6. Avatar  

    Reaver Con

    May 7, 2015 @ 10:32 pm

    I am having issues with the …method(named(“toString”). Using ByteBuddy 0.6.5. and Asm – 5.0.3.jar The “named” method is undefined. Even copying the example above does not work. What am I missing?

  7. Avatar  

    ashleel baba

    June 8, 2015 @ 6:38 am

    Raphael can I add my analysis info gathered using this ? Like I get to devirtualise some method based on my new method (lets sy i did class hieracrchy analysis or some other variations ?) where should I patch that info that I have gathered during that phase ? And how to check the same . Thanks

  8. Avatar  

    Rafael Winterhalter

    September 23, 2015 @ 8:58 am

    You need to statically import the ElementMatchers class’s predefined interceptors.

  9. Avatar  

    Rafael Winterhalter

    September 23, 2015 @ 9:00 am

    Hi, get in touch with me on GitHub and I will be more than happy to look at your analysis. Anything that improves Byte Buddy will be merged!

  10. Avatar  

    papa minos

    November 13, 2015 @ 8:31 am

    Great post, Rafael and looks very interesting.
    What is about plain old :) hello world project ?

  11. Avatar  

    papa minos

    November 13, 2015 @ 8:39 am

    Already found it on your github page .
    Great work

RSS feed for comments on this post.

Leave a comment