Search code examples
javaclassloader

How can I run my code upon class load?


Is there a feasible way to get my own code run whenever any class is loaded in Java, without forcing the user explicitly and manually loading all classes with a custom classloader?

Without going too much into the details, whenever a class implementing a certain interface read its annotation that links it with another class, and give the pair to a third class.

Edit: Heck, I'll go to details: I'm doing an event handling library. What I'm doing is having the client code do their own Listener / Event pairs, which need to be registered with my library as a pair. (hm, that wasn't that long after all).

Further Edit: Currently the client code needs to register the pair of classes/interfaces manually, which works pretty well. My intent is to automate this away, and I thought that linking the two classes with annotations would help. Next, I want to get rid of the client code needing to keeping the list of registrations up to date always.

PS: The static block won't do, since my interface is bundled into a library, and the client code will create further interfaces. Thus, abstract classes won't do either, since it must be an interface.


Solution

  • If you want to base the behavior on an interface, you could use a static initializer in that interface.

    public interface Foo{
    
        static{
            // do initializing here
        }
    
    }
    

    I'm not saying it's good practice, but it will definitely initialize the first time one of the implementing classes is loaded.

    Update: static blocks in interfaces are illegal. Use abstract classes instead!

    Reference:


    But if I understand you right, you want the initialization to happen once per implementing class. That will be tricky. You definitely can't do that with an interface based solution. You could do it with an abstract base class that has a dynamic initializer (or constructor), that checks whether the requested mapping already exists and adds it if it doesn't, but doing such things in constructors is quite a hack.

    I'd say you cleanest options are either to generate Code at build time (through annotation processing with apt or through bytecode analysis with a tool like asm) or to use an agent at class load time to dynamically create the mapping.


    Ah, more input. Very good. So clients use your library and provide mappings based on annotations. Then I'd say your library should provide an initializer method, where client code can register classes. Something like this:

    YourLibrary.getInstance().registerMappedClasses(
        CustomClass1.class,
        CustomClass2.class,
        CustomClass3.class,
        CustomClass4.class
    )
    

    Or, even better, a package scanning mechanism (example code to implement this can be found at this question):

    YourLibrary.getInstance().registerMappedClassesFromPackages(
        "com.mycompany.myclientcode.abc",
        "com.mycompany.myclientcode.def"
    )
    

    Anyway, there is basically no way to avoid having your clients do that kind of work, because you can't control their build process nor their classloader for them (but you could of course provide guides for classloader or build configuration).