【问题标题】:Can a method call be instrumented with ByteBuddy instead of the called method?可以使用 ByteBuddy 而不是被调用的方法来检测方法调用吗?
【发布时间】:2019-09-05 15:15:30
【问题描述】:

我想替换一些 AspectJ 代码来保护对 java.lang.System 的调用不受某些用户代码的影响。 java.lang.System 不能/不应该被检测。

使用 AspectJ,解决方案是像以下示例一样检测调用代码。应该保护的代码将被检测,而允许的代码则不被检测。

@Around("call(public long java.lang.System.currentTimeMillis()) && within(io.someuserdomain..*) && !within(io.someotherdomain..*))
def aroundSystemcurrentTimeMillis(wrapped: ProceedingJoinPoint): Long = {
      throw new IllegalStateException("must not call System.currentTimeMillis in usercode")
}

有没有办法使用 ByteBuddy 做同样的事情?到目前为止,我只找到了有关如何检测被调用者而不是调用者的示例。

【问题讨论】:

  • 为什么要用ByteBuddy 替换AspectJ? AspectJ 运行时要小得多,并且没有其他依赖项。您想通过切换工具来解决什么问题?
  • 对不起,我忘了量化我之前的陈述:AspectJ 运行时有 120K,ByteBuddy 打包依赖项有 3.2M。比较当前版本的 JAR,JAR 大小相差 27.5 倍,有利于 AspectJ。这并不意味着它是更好的工具,但对于您想要做的事情,它非常适合。如果使用编译时编织,您甚至可以在一个方面声明编译器警告或错误,以便在方面检测到非法方法调用时使编译失败。如果 CTW 是一个选项而不是 LTW,我会这样做,因为在运行时检测它是次优的。
  • @kriegaex 好问题。我们正在使用另一个从 aspectj 切换到 bytebuddy (kamon) 的库。在我们的代码库中,我们谨慎地使用 aspectj,并希望避免维护多个检测框架。至于讨论的用例——如果这是我唯一需要仪器的地方,我会选择 aspectj,因为它允许更大的灵活性。但是我当然有偏见,因为我经常使用 aspectj,而且我是 bytebuddy 的新手。在我从 aspectj 转换为 bytebuddy 的其他地方,bytebuddy 中的检测声明的类型安全性比 aspectj 更好。
  • 我很惊讶你这么说类型安全。实际上,我从那些没有足够经验正确使用 AspectJ 的人那里看到了很多不好的方面代码,所以也许这就是它的来源。话虽如此,并承认我的支持 AspectJ 的偏见,因为我一直在使用它,但我确实同意 ByteBuddy 是一个强大的工具,并且毫无疑问,你也可以用它优雅地解决你的问题。 Rafael 的回答在我看来非常好,他在 SO 上提供了大力支持,他是 ByteBuddy 的作者。 :-) 祝你好运!

标签: java scala aspectj byte-buddy


【解决方案1】:

您目前可以通过注册 MemberSubstitution 来替换方法或字段访问,但与 AspectJ 相比,这些功能仍然有限。例如,不可能像您的示例代码中那样抛出异常。但是,您可以委托给包含引发异常的代码的方法:

MemberSubstitution.relaxed()
  .method(named("currentTimeMillis"))
  .replaceWith(MyClass.class.getMethod("throwException"))
  .in(any());

上述替换将用对以下成员的调用替换任何方法调用:

public class MyClass {
  public static long throwException() {
    throw new IllegalStateException();
  }
}

替换将应用于应用访问者的任何方法。您可以注册 AgentBuilder.Default 来构建 Java 代理,或者查看 Byte Buddy 的构建插件。

【讨论】:

  • 感谢您的快速回复!不幸的是,我坚持应用这个例子。 new ByteBuddy().redefine(EvilCode.getClass).visit(MemberSubstitution.relaxed.method(ElementMatchers.hasMethodName("currentTimeMillis")).replaceWith(Thrower.getClass.getMethod("throwException")).on(ElementMatchers.any())).make() 这抛出了java.lang.IllegalStateException: Cannot invoke public long Thrower$.throwException() on [] 详细要点在这里:gist.github.com/danischroeter/29c418d9f0b73af4d901e93fff3ba2a6
  • 嗯,IllegalStateException 似乎是使用 scala 对象中的静态方法的问题,其中 ByteBuddy 发现参数不兼容,因为对象的 INSTANCE 字段...通过声明一个普通的 java 类没有抛出异常...
  • 我让示例运行没有失败,但我的测试仍然失败(仪器没有接听电话)gist.github.com/danischroeter/29c418d9f0b73af4d901e93fff3ba2a6 bytebuddy 确实改变了一些东西:OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 但 Thrower 没有被调用 Expected exception java.lang.RuntimeException to be thrown, but no exception was thrown
  • 您是否在代理中注册了 AgentBuilder.Listener 以查看该类是否正确检测?
猜你喜欢
  • 1970-01-01
  • 2021-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
  • 2014-03-25
  • 2021-04-12
  • 2018-11-08
相关资源
最近更新 更多