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

时间:2016-06-08 11:19:01

标签: java design-patterns

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

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

public class Example {

private int data;

public void function1(){

}

public void function2(){

}

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

因为编程似乎不好,所以我不想在其他所有功能的开头调用refresh。由于其他人也会参与这个项目的工作,因此有人会延长通话时间,并且不会拨打refresh

JUnit为@Before - 注释提供了解决方案。有没有办法在其他课程中这样做?

顺便说一下:如果你知道一个编程模式,除了在每次调用任何函数时执行函数以另一种方式解决这个问题,那也是非常有用的!

7 个答案:

答案 0 :(得分:2)

使用动态代理,您可以在其中过滤到应该调用特定“之前”方法的方法。并在发送呼叫之前在这些情况下调用它。请参阅How do I intercept a method invocation with standard java features (no AspectJ etc)?

的答案

<强>更新

需要为代理分隔接口。 refresh()方法不能保持私有。它必须是公共的,并且可以从代理中调用接口的一部分(这里不是很好)。

package CallBefore;

public interface ExampleInterface {
    void function1();

    void function2();

    void otherFunction();

    void refresh();
}

您的类实现了该接口:

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");
    }
}

执行该技巧的代理。它过滤所需的方法并调用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;
    }
}

用法:

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();
    }
}

输出:

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

答案 1 :(得分:2)

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

您需要的第一件事就是为您的班级提供接口。

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);
    }
}

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

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;
    }
}

毕竟您的客户端代码看起来像这样。

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");

此代码的输出为

Before
Hello Mehmet
After
finally
Before
Bye Mehmet
After
finally

答案 2 :(得分:1)

我会为每个字段定义getter并在getter中进行刷新。如果你想完全避免对私有字段的未刷新访问,请将它们放在超类中(与调用刷新的getter一起)。

根据您的项目结构,为定期刷新的所有数据引入单独的类也是明智的。它可以提供getter并避免任何人访问未刷新的字段。

答案 3 :(得分:1)

不在Java SE中,但如果您使用的是Java EE,则可以使用interceptors

对于独立应用程序,您可以考虑使用字节码操作框架,如javassist

答案 4 :(得分:0)

If (sourceString).Contains($"< div class={QUOTE}panel-base xbl{QUOTE} style={QUOTE}background-color: RGB(236, 255, 236);{QUOTE}><div class={QUOTE}marshmallowLogo{QUOTE} id={QUOTE}xboxLogo{QUOTE}>Xbox 360</div><center><span class={QUOTE}statusSpan{QUOTE} style={QUOTE}color green;{QUOTE}>Up</span></center>") Then End If 可以使用String.Format方法。访问$""方法,而不是使用protected getter字段。子类只会看到data,并且每次都会更新数据。

getData

答案 5 :(得分:0)

最好编写另一个方法,该方法将受到保护(子类可访问),它将首先调用refresh方法,然后调用该函数。

这样,每次调用函数之前都会刷新数据(根据您的要求)。 例如:

protected void callFunction1(){
refresh();
function();
    }

谢谢, 拉杰什

答案 6 :(得分:0)

在这种情况下你应该使用Decorator。装饰器是拦截器之类的好选择。示例:https://msdn.microsoft.com/en-us/library/dn178467(v=pandp.30).aspx