将静态方法添加到 class 并委托给现有方法

Add a static method to class and delegate to an existing method

这是我的。代理人:

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 是以这种方式构建的背景。