如何 "proxy" Java 中的方法

How to "proxy" a method in Java

首先,我不确定如何最好地表达我的解决方案,所以如果我有时似乎在喋喋不休,请考虑一下。

我希望在不触及物理代码的情况下修改库中的一个接口,

public interface ProxiedPlayer {
    // .. other code
    public void setPermission(String permission, boolean state);
}

我已经编写了一个第三方库来处理权限,并且必须挂接到我的 API 来编辑权限可能是一些开发人员不想采取的步骤。所以我问,当 setPermission 被调用时,是否可以让它调用我调用我的库中的适当方法来处理权限设置 whilst 忽略预编程代码或不是吗?

这是完整的 interface 我正在尝试代理。

我已经查看了 Java 代理 class ,但您似乎首先需要一个您尝试代理的对象的实例。 鉴于可以随时调用该方法,我不认为这是我的解决方案 但很乐意接受更正。

我无法控制实现 ProxiedPlayer 接口的 classes 的实例化。

编辑:无知的我,我可以订阅几个事件,在那里可以获得播放器的实例,这是尝试代理该方法的合适位置吗?当玩家加入服务器并且可以获取玩家的实例时,将触发这些事件之一。

是否需要为 ProxiedPlayer 接口的每个实例调用代理代码,或者是否可以以更简单的方式简单地代理方法的每次调用?

我的库是一个插件,在其他所有必要的东西都加载完成后加载。

编辑#2:

import net.md_5.bungee.api.connection.ProxiedPlayer;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class InvocationProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ProxiedPlayer player = (ProxiedPlayer) proxy;
        if(method.getName().equals("setPermission")) {
            // Call my code here?
        }
        return method.invoke(player, args);
    }
}

按照我上面的方法行事,还是我完全找错了树?

如果你不想触及原始来源,那么你只能通过使用 Java 代理来解决这个问题,该代理重新定义任何实现 ProxiedPlayer 接口的 class 以强制执行在调用实际方法之前进行安全检查。已经提到 AspectJ 和加载时编织代理作为一个可能的解决方案,但您也可以使用我的库 Byte Buddy:

实现纯 Java 解决方案
public class InterceptionAgent {
  public static void premain(String arguments, 
                             Instrumentation instrumentation) {
    new AgentBuilder.Default()
      .rebase(isSubtypeOf(ProxiedPlayer.class))
      .transform(new AgentBuilder.Transformer() {
        @Override
        public DynamicType.Builder transform(DynamicType.Builder builder) {
          return builder.method(named("setPermission"))
                        .intercept(MethodDelegation.to(MyInterceptor.class)
                      .andThen(SuperMethodInvocation.INSTANCE));
        }
      }).installOn(instrumentation);
  }
}

使用此代理,您或多或少地指定要重新定义任何 ProxiedPlayer 的子类型 class 以重新定义名为 setPermisson 的(任何)方法,以便调用 MyInterceptor(这将是您的代码)并随后调用原始实现。

请注意,建议的实现假定所有实现 ProxiedPlayer 的 class 都实现了此接口的方法,并且此签名只有一个方法。这可能太简单了,但它显示了前进的方向。