Java:是否可以在调用其他函数之前始终执行某个函数?(就像 JUnit 中的 @Before)

Rui*_*uik 10 java design-patterns

有没有办法在调用类的任何其他函数之前始终执行函数?

我有一个类,我需要在调用任何函数之前总是刷新一些字段:

public class Example {

private int data;

public void function1(){

}

public void function2(){

}

//@BeforeOtherFunction
private void refresh(){
    // refresh data
}
}
Run Code Online (Sandbox Code Playgroud)

因为它似乎是糟糕的编程,所以我不想refresh在每个其他函数的开头调用。由于其他人也将在此项目上工作,因此存在危险,即有人扩展了调用而没有调用refresh

JUnit 有一个带有@Before-Annotation的解决方案。有没有办法在其他课程中做到这一点?

顺便说一句:如果你知道一种编程模式,它以另一种方式解决这个问题,而不是每次调用任何函数时都执行一个函数,那也会非常有帮助!

Las*_*rdi 8

使用动态代理,您可以在其中过滤到应调用特定“before”方法之前的那些方法。在这些情况下,在发送呼叫之前调用它。请参阅如何拦截具有标准 Java 功能(无 AspectJ 等)的方法调用的答案

更新:

需要为代理分离一个接口。refresh() 方法不能保持私有。它必须是公共的并且是接口的一部分(这在这里不好)才能从代理调用。

package CallBefore;

public interface ExampleInterface {
    void function1();

    void function2();

    void otherFunction();

    void refresh();
}
Run Code Online (Sandbox Code Playgroud)

您的课程实现了该接口:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

可以解决问题的代理。它过滤所需的方法并调用 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

refresh() has been called
function1() has been called
refresh() has been called
function2() has been called
otherFunction() has been called
refresh() has been called
Run Code Online (Sandbox Code Playgroud)


Meh*_*taş 6

这可能无法解决您的确切问题,但如果您被允许考虑重新设计,那么至少可以作为一个起点。下面是一个简单的实现,但通过一些小修改,我相信您可以实现更优雅的解决方案。顺便说一句,这称为动态代理模式。

您需要的第一件事是您的类的接口。

public interface Interface {
    void hello(String name);
    void bye(String name);
}

public class Implementation implements Interface {
    @Override
    public void hello(String name) {
        System.out.println("Hello " + name);
    }

    @Override
    public void bye(String name) {
        System.out.println("Bye " + name);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后java.lang.reflect.Proxy班级来帮忙。该类能够在运行时为给定接口创建实例。它还接受一个InvocationHandler可以帮助您捕获方法调用的 an ,看起来像这样。

public class InvocationHandlerImpl implements InvocationHandler {
    private final Object instance;

    public InvocationHandlerImpl(Object instance) {
        this.instance = instance;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;
        try {
            System.out.println("Before");
            result = method.invoke(instance, args);
            System.out.println("After");
        } catch (Exception e){
            e.printStackTrace();
            throw e;
        } finally {
            System.out.println("finally");
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

毕竟您的客户端代码将如下所示。

Interface instance = new Implementation();

Interface proxy = (Interface)Proxy.newProxyInstance(
        Interface.class.getClassLoader(),
        new Class[] { Interface.class },
        new InvocationHandlerImpl(instance));

proxy.hello("Mehmet");

proxy.bye("Mehmet");
Run Code Online (Sandbox Code Playgroud)

该代码的输出是

Before
Hello Mehmet
After
finally
Before
Bye Mehmet
After
finally
Run Code Online (Sandbox Code Playgroud)