javadesign-patterns

Java: Is it possible to always execute a certain function before other functions are called? (Like @Before in JUnit)


Is there a way to always execute a function before any other function of a class is called?

I have a class where I need to refresh some fields always before any function is called:

public class Example {

private int data;

public void function1(){
    
}

public void function2(){
    
}

//@BeforeOtherFunction
private void refresh(){
    // refresh data
}
}

Because it seems to be bad programming, I don't want to call refresh at the beginning of every other function. Since other persons are going to work on this project as well, there would be the danger, that somebody extends the calls and doesn't call refresh.

JUnit has a solution for this with the @Before-Annotation. Is there a way to do this in other classes as well?

And by the way: If you know a programming pattern which solves this problem in another way than executing a function every time any function is called, that would be very helpful, too!


Solution

  • Use a dynamic proxy in which you can filter to those methods before which your specific "before" method should be called. And call it in those cases before dispatching the call. Please see the answer from How do I intercept a method invocation with standard java features (no AspectJ etc)?

    UPDATE:

    An interface is needed to be separated for the proxy. The refresh() method cannot remain private. It must be public and part of the interface (which is not nice here) to be able to be called from the proxy.

    package CallBefore;
    
    public interface ExampleInterface {
        void function1();
    
        void function2();
    
        void otherFunction();
    
        void refresh();
    }
    

    Your class implements that interface:

    package CallBefore;
    
    public class Example implements ExampleInterface {
    
        @Override
        public void function1() {
        System.out.println("function1() has been called");
        }
    
        @Override
        public void function2() {
        System.out.println("function2() has been called");
        }
    
        @Override
        public void otherFunction() {
        System.out.println("otherFunction() has been called");
        }
    
        @Override
        public void refresh() {
        System.out.println("refresh() has been called");
        }
    }
    

    The proxy which does the trick. It filters the needed methods and calls refresh().

    package CallBefore;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class ExampleProxy implements InvocationHandler {
    
        private ExampleInterface obj;
    
        public static ExampleInterface newInstance(ExampleInterface obj) {
        return (ExampleInterface) java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
            obj.getClass().getInterfaces(), new ExampleProxy(obj));
        }
    
        private ExampleProxy(ExampleInterface obj) {
        this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result;
        try {
            if (m.getName().startsWith("function")) {
            obj.refresh();
            }
            result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
        }
        return result;
        }
    }
    

    The usage:

    package CallBefore;
    
    public class Main {
    
        public static void main(String[] args) {
    
        ExampleInterface proxy = ExampleProxy.newInstance(new Example());
        proxy.function1();
        proxy.function2();
        proxy.otherFunction();
        proxy.refresh();
        }
    }
    

    Output:

    refresh() has been called
    function1() has been called
    refresh() has been called
    function2() has been called
    otherFunction() has been called
    refresh() has been called