Search code examples
java

Call a method any time other methods are called


Is there any way to make a sort of "supermethod" that is called every time a method is called, even for non-defined methods? Sort of like this:

public void onStart() {
    System.out.println("Start");
}

public void onEnd() {
    System.out.println("End");
}

public SuperMethod superMethod() {
    System.out.println("Super");
}

// "Start"
// "Super"
onStart();

// "End"
// "Super"
onEnd();

// "Super"
onRun();

Edit - Specifics: I have a library that updates a lot and gets reobfuscated on each update. To make my workflow easier I am making my program automatically update the library (required to do what I want it to do, I won't go that specific on why, but my program will work with future updates) and I have the obfuscation mappings download with the library, I want to make a sort of proxy called Library for example and then when I call Library.getInstance() it will get the obfuscation mapping for getInstance() and call the library's method getInstance() or abz as it is mapped to at this current moment in time.


Solution

  • Here is an implementation in pure Java using the Proxy class:

    import java.lang.reflect.*;
    import java.util.*;
    
    public class Demo
    {
        public static void main(String[] args)
        {
            Map<String, String> map = new HashMap<String, String>();
            map.put("onStart", "abc");
            map.put("onEnd", "def");
            Library library = new LibraryProxy(map, new LibraryImpl()).proxy();
            library.onStart();
            library.onEnd();
            library.onRun();
        }
    }
    
    interface Library
    {
        void onStart();
        void onEnd();
        void onRun();
    }
    
    class LibraryImpl
    {
        public void abc() { System.out.println("Start"); }
        public void def() { System.out.println("End"); }
    }
    
    class LibraryProxy implements InvocationHandler
    {
        Map<String, String> map;
        Object impl;
    
        public LibraryProxy(Map<String, String> map, Object impl)
        {
            this.map = map;
            this.impl = impl;
        }
    
        public Library proxy()
        {
            return (Library) Proxy.newProxyInstance(Library.class.getClassLoader(),
                new Class[] { Library.class }, this);
        }
    
        @Override
        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
        {
            Object res = null;
            String name = map.get(m.getName());
            if (name == null) {
                System.out.println("[" + m.getName() + " is not defined]");
            } else {
                m = impl.getClass().getMethod(name, m.getParameterTypes());
                res = m.invoke(impl, args);
            }
            System.out.println("super duper");
            return res;
        }
    }
    

    Output:

    Start
    super duper
    End
    super duper
    [onRun is not defined]
    super duper