java - 向类添加静态方法并委托(delegate)给现有方法

标签 java spring byte-buddy

这就是我所拥有的。代理:

public static void premain(String args, Instrumentation inst) throws Exception {
    new AgentBuilder.Default()
            .type(ElementMatchers.named("org.springframework.boot.SpringApplication"))
            .transform(new SpringApplicationTransformer())
            .installOn(inst);
}

和变压器:

@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
        ClassLoader classLoader, JavaModule module) {
    try {
        builder = builder
                .defineMethod("run", classLoader.loadClass(
                        "org.springframework.context.ConfigurableApplicationContext"),
                        Modifier.PUBLIC | Modifier.STATIC)
                .withParameter(Class.class).withParameter(String[].class)
                .intercept(MethodCall
                        .invoke(named("run").and(isStatic()
                                .and(takesArguments(Object.class, String[].class))))
                        .withAllArguments());
        return builder;
    }
    catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

我可以在调试器中看到变压器被调用并成功完成,但我仍然无法调用该方法:

$ java -javaagent:target/spring-boot-legacy-agent-0.0.1-SNAPSHOT-agent.jar -jar target/spring-boot-legacy-agent-0.0.1-SNAPSHOT.jar --thin.profile=old
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.wrapper.ThinJarWrapper.launch(ThinJarWrapper.java:118)
at org.springframework.boot.loader.wrapper.ThinJarWrapper.main(ThinJarWrapper.java:90)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
at org.springframework.boot.loader.thin.ThinJarLauncher.launch(ThinJarLauncher.java:186)
at org.springframework.boot.loader.thin.ThinJarLauncher.main(ThinJarLauncher.java:133)
... 6 more
Caused by: java.lang.NoSuchMethodError: org.springframework.boot.SpringApplication.run(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;
at com.example.AgentApplication.main(AgentApplication.java:10)
... 15 more

向代理构建器添加监听器可以让我获得更多信息:

ava.lang.IllegalStateException: class org.springframework.boot.SpringApplication does not define exactly one virtual method for (name(equals(run)) and hasParameter(hasTypes(erasures(containing(is(class java.lang.Object;), is(class [Ljava.lang.String;))))))
at net.bytebuddy.implementation.MethodCall$MethodLocator$ForElementMatcher.resolve(MethodCall.java:668)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2002)
...

所以看起来方法匹配器不匹配。 有一个具有这些参数类型的静态方法,但在过滤时它不在列表中。我一定是缺少静态方法的某些东西吗?

我确实设法让它与 javassist 一起工作,但我想了解为什么 byte-buddy 不起作用。

最佳答案

匹配器 API 仅用于动态检测虚拟调用,其中这种类型相关的检测有意义,主要是由于泛型。然而,对于静态方法,您确实知道声明方法的确切类型以及该方法的类型,因此您应该提供对静态方法的引用:

MethodCall.invoke(typeDescription
                   .getDeclaredMethods()
                   .filter(named("run")
                     .and(isStatic()
                     .and(takesArguments(Object.class, String[].class)))
                   .getOnly()).withAllArguments())

您所使用的方法的 javadoc 中也提到了这一点:

Invokes a unique virtual method of the instrumented type that is matched by the specified matcher.

不过,我应该提供更多背景知识来说明为什么 API 是这样构建的。

关于java - 向类添加静态方法并委托(delegate)给现有方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49956509/

相关文章:

java - 方法被拦截两次,即使它被调用一次

java - 我需要有关在 Java GUI 上添加函数的帮助

java - 使用可点击图像的菜单?

java - 设置和获取 double 组

java - ByteBuddy - 转换所有方法

java - 在项目中使用 mockito-inline 会抛出 "Mockito cannot mock this class"错误

java - 声明为 Final 的经典变量是否包含与非 Final 变量不同的内存段?

java - 外键对 POST 的结果不起作用,但 GET 有效

spring - HTTP 状态 401 - 需要完全身份验证才能访问此资源

java - 如何在 Spring GET servlet 中保留空值?