Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a method any time other methods are called

Tags:

java

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.

like image 506
Jordan Doyle Avatar asked Nov 08 '13 14:11

Jordan Doyle


2 Answers

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
like image 66
tom Avatar answered Oct 29 '22 04:10

tom


Sure you can do this, not with standard java but with AspectJ

Here is a simple example:

Aspect with an after-advice

package net.fsa.aspectj.test;


public aspect SuperMethdAspect {

    pointcut afterPointCut() : execution(public * com.my.pack.age.MyClass.*(..));

    after() : afterPointCut() {
        System.out.println("Super");
    }
}

You target class

package com.my.pack.age;

public class MyClass {

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

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

And finally some test app

package net.fsa.aspectj.test;

import com.my.pack.age.MyClass;

public class MyApp {

    public static void main(String... args) {
        MyClass myClass = new MyClass();
        myClass.onStart();
        myClass.onEnd();
    }
}

Output

Start
Super
End
Super
like image 39
A4L Avatar answered Oct 29 '22 04:10

A4L