如何"代理" Java中的一个方法

时间:2015-02-01 17:34:02

标签: java reflection

首先,我不确定如何最好地说出我的解决方案,所以如果我有时会唠叨,那么请考虑一下。

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

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

我已经编写了第三方库来处理权限,并且必须挂钩我的API来编辑权限可能是一些开发人员不想采取的步骤。所以我要求调用setPermission时可以调用我的调用我的库中的相应方法来处理权限设置忽略预编程的代码?< / p>

以下是我试图代理的完整interface

我已经研究过Java Proxy类但是你似乎需要一个你想要代理的对象的实例。鉴于我可以在任何时候调用该方法不相信这是我的解决方案但很高兴能够得到纠正。

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

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

是否需要为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);
    }
}

我的上述工作是否有所作为,或者我是否完全咆哮错误的树?

1 个答案:

答案 0 :(得分:2)

如果您不想触及原始源,那么您只能通过使用Java代理解决此问题,该代理重新定义实现ProxiedPlayer接口的任何类,以在调用实际方法之前强制执行安全检查。已经提到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子类的任何类,以重新定义名为setPermisson的(任何)方法,以便调用MyInterceptor (这将是您的代码)并随后调用原始实现。

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