How to correctly add a method to an existing class with Byte Buddy

1,539 views
Skip to first unread message

Anwar Ludin

unread,
Apr 16, 2022, 8:27:43 AM4/16/22
to Byte Buddy
I have asked this question on StackOverflow but this group might be a better forum:


From my understanding, you cannot add methods to a class once it has been loaded by a classloader (in other words redefine a class). I have tried different approaches including using an agent but to no avail.

I have a base class which defines a couple of static methods in order to implement the active record pattern:

public class ActiveRecord {    
    private Long id;

    // getters and setters
  
    private static IllegalStateException implementationMissing() {
        return new IllegalStateException(
                "This method must be overridden in subclasses");
    }
   
    public static Long count(){
        throw implementationMissing();
    }

    public static  <T extends ActiveRecord> List<T> findAll(){
        throw implementationMissing();
    }
    public static <T extends ActiveRecord> T findById(Object id){
        throw implementationMissing();
    }
   
    public static void deleteAll(){
        throw implementationMissing();
    }
   
    public void save(){
        throw implementationMissing();
    }
   
    public void delete(){
        throw implementationMissing();
    }
}

For any given class extending active ActiveRecord, I am trying to implement the methods defined by ActiveRecord using ByteBuddy. For example:

class MapText extends ActiveRecord {}

@Test
    void testRedefine(){
        ByteBuddyAgent.install();
        new ByteBuddy().redefine(MapText.class)
                .defineMethod("save", void.class, Visibility.PUBLIC)
                .intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
                .defineMethod("count", Long.class, Visibility.PUBLIC)
                .intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
                .make()
                .load(MapText.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
       
        MapText mapText = new MapText();
        mapText.save();
        MapText.count();
    }

However the code displayed above generates the following exception:

java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method

I also tried another approach, which consists of using a type pool:

// Spring boot application 
public static void main(String[] args) {
        TypePool typePool = TypePool.Default.ofSystemLoader();
        new ByteBuddy()
                .redefine(typePool.describe("pkg.MapText").resolve(),
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .defineMethod("save", void.class, Visibility.PUBLIC)
                .intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
                .defineMethod("count", Long.class, Visibility.PUBLIC)
                .intercept(MethodDelegation.to(ActiveRecordInterceptor.class))
                .make()
                .load(ClassLoader.getSystemClassLoader());
        SpringApplication.run(AdmService.class, args);
    }

However this time I get the following exception:
java.lang.IllegalStateException: Class already loaded: pkg.MapText

In both cases it seems that the class is already loaded by the class loader and I cannot redefine it. 

My understanding is that my only option would be to redefine the classes at compile time using the maven plugin 


However I can't find any sample code showing how to proceed (I am unfamiliar with maven plugins). 

Any help explaining how to proceed in order to make this work would be greatly appreciated (with a last resort using a plugin)! 

Thanks,
Anwar 


Scott Babcock

unread,
Aug 20, 2022, 4:09:34 PM8/20/22
to Byte Buddy
From the Stack Overflow thread, it appears that you got your implementation to work. Can you share your solution? This could be very helpful to others who are struggling with the same issue.

Rafael Winterhalter

unread,
Aug 21, 2022, 10:20:33 AM8/21/22
to Scott Babcock, Byte Buddy
It's not possible to retransform an already loaded class to include new methods or fields. It's a limit set by the JVM.

What are you trying to achieve?

--
You received this message because you are subscribed to the Google Groups "Byte Buddy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to byte-buddy+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/byte-buddy/5137310c-daed-44a9-ab03-61578d0ea9a4n%40googlegroups.com.

Scott Babcock

unread,
Aug 21, 2022, 1:37:35 PM8/21/22
to Byte Buddy
The Stack Overflow thread related to this post indicates that the OP was able to implement a solution that behaved as expected. However, the shape of this solution was never shared. I was merely suggesting that the OP could share his solution so other could learn from his experience.

Rafael Winterhalter

unread,
Aug 21, 2022, 1:40:28 PM8/21/22
to Scott Babcock, Byte Buddy
I assume a class with the same name was loaded by a child-first loader.

Reply all
Reply to author
Forward
0 new messages