向类添加静态方法并委托给现有方法

时间:2018-04-21 13:55:23

标签: 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)
...

所以看起来matcher方法不匹配。那里的一个带有这些参数类型的静态方法,但是当它被过滤时它不在列表中。我必须遗漏一些静态方法吗?

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

1 个答案:

答案 0 :(得分:3)

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

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

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

  

调用由指定匹配器匹配的检测类型的唯一虚拟方法。

但是,我应该更多地介绍为什么以这种方式构建API。