如何防止 Java 代理中定义的 Lambda 表达式破坏它所附加的应用程序?
How to prevent Lambda expressions defined in a Java agent break the application it is attached to?
如果我在代理中声明此建议:
public static class SequenceAdvice {
@Advice.OnMethodEnter
static void enter(@Advice.This Object thiz,
@Advice.Origin Method method,
@Advice.AllArguments Object... args) {
StackWalker walker =
StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
walker.forEach(sf ->
System.out.println(sf.getClassName() + "." + sf.getMethodName())
);
}
}
as javac
将 lambda 表达式编译成私有方法(至少在 OpenJDK 11 中):
public class SequenceAgent$SequenceAdvice {
...
private static void lambda$enter[=12=](java.lang.StackWalker$StackFrame);
...
}
当代理附加到程序并执行程序时,导致程序崩溃:
Exception in thread "main" java.lang.IllegalAccessError:
class DemoController tried to access private method
SequenceAgent$SequenceAdvice.lambda$enter[=13=](
Ljava/lang/StackWalker$StackFrame;)V
(DemoController and SequenceAgent$SequenceAdvice
are in unnamed module of loader 'app')
at DemoController.getDemos(DemoController.java)
at DemoMain.main(DemoMain.java:13)
理想情况下,我不喜欢使用对象而不是 lambda 表达式来解决这个问题:
public static class SequenceAdvice {
public static Consumer<StackWalker.StackFrame> SF_CONSUMER =
new Consumer<>() {
@Override
public void accept(StackWalker.StackFrame sf) {
System.out.println(sf.getClassName() + "." + sf.getMethodName());
}
};
@Advice.OnMethodEnter
static void enter(@Advice.This Object thiz,
@Advice.Origin Method method,
@Advice.AllArguments Object... args) {
StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
walker.forEach(SF_CONSUMER);
}
}
自定义许可安全策略似乎无法解决此错误:
grant {
permission java.security.AllPermission;
};
有没有办法暂时禁用此类安全检查(例如 "access to private method")?
您不能使用建议中的 lambda 表达式。 lambda 表达式将成为未暴露给目标 class 的建议 class 的一部分。相反,您将需要定义一个实用程序 class,它在 public 方法中定义 lambda 表达式代码,并将这些方法作为方法引用进行引用。
然后您必须:
- 通过
Instrumentation
. 将此 class 添加到 bootstrap class 加载程序
- 通过 Byte Buddy 的
Injector
. 将此 class 添加到检测 class 的 class 加载器
这样,引用可用于检测 class 并且可以执行。
如果我在代理中声明此建议:
public static class SequenceAdvice {
@Advice.OnMethodEnter
static void enter(@Advice.This Object thiz,
@Advice.Origin Method method,
@Advice.AllArguments Object... args) {
StackWalker walker =
StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
walker.forEach(sf ->
System.out.println(sf.getClassName() + "." + sf.getMethodName())
);
}
}
as javac
将 lambda 表达式编译成私有方法(至少在 OpenJDK 11 中):
public class SequenceAgent$SequenceAdvice {
...
private static void lambda$enter[=12=](java.lang.StackWalker$StackFrame);
...
}
当代理附加到程序并执行程序时,导致程序崩溃:
Exception in thread "main" java.lang.IllegalAccessError:
class DemoController tried to access private method
SequenceAgent$SequenceAdvice.lambda$enter[=13=](
Ljava/lang/StackWalker$StackFrame;)V
(DemoController and SequenceAgent$SequenceAdvice
are in unnamed module of loader 'app')
at DemoController.getDemos(DemoController.java)
at DemoMain.main(DemoMain.java:13)
理想情况下,我不喜欢使用对象而不是 lambda 表达式来解决这个问题:
public static class SequenceAdvice {
public static Consumer<StackWalker.StackFrame> SF_CONSUMER =
new Consumer<>() {
@Override
public void accept(StackWalker.StackFrame sf) {
System.out.println(sf.getClassName() + "." + sf.getMethodName());
}
};
@Advice.OnMethodEnter
static void enter(@Advice.This Object thiz,
@Advice.Origin Method method,
@Advice.AllArguments Object... args) {
StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
walker.forEach(SF_CONSUMER);
}
}
自定义许可安全策略似乎无法解决此错误:
grant {
permission java.security.AllPermission;
};
有没有办法暂时禁用此类安全检查(例如 "access to private method")?
您不能使用建议中的 lambda 表达式。 lambda 表达式将成为未暴露给目标 class 的建议 class 的一部分。相反,您将需要定义一个实用程序 class,它在 public 方法中定义 lambda 表达式代码,并将这些方法作为方法引用进行引用。
然后您必须:
- 通过
Instrumentation
. 将此 class 添加到 bootstrap class 加载程序
- 通过 Byte Buddy 的
Injector
. 将此 class 添加到检测 class 的 class 加载器
这样,引用可用于检测 class 并且可以执行。