Byte Buddy Advice.OnMethodExit:构造函数重新转换

时间:2017-06-25 14:13:43

标签: java agent byte-buddy

我正在尝试创建拦截FileInputStream / FileOutputStream构造函数的Java代理:

import java.io.*;
import java.lang.instrument.Instrumentation;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.InitializationStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.Listener;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.TypeStrategy;
import net.bytebuddy.asm.Advice;
import static net.bytebuddy.dynamic.ClassFileLocator.CLASS_FILE_EXTENSION;
import static net.bytebuddy.matcher.ElementMatchers.*;
import net.bytebuddy.matcher.StringMatcher;

public class Agent {

    private static final List<Class<?>> BOOTSTRAP_CLASSES = Arrays.asList(
       Interceptor.class
    );

    private Agent() {
    }

    public static void premain(String arg, Instrumentation instrumentation) {
        injectBootstrapClasses(instrumentation);
        new AgentBuilder.Default()
            .with(RedefinitionStrategy.RETRANSFORMATION)
            .with(InitializationStrategy.NoOp.INSTANCE)
            .with(TypeStrategy.Default.REDEFINE)
            .ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
            .with(new Listener.Filtering(
                new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
                    .or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
                Listener.StreamWriting.toSystemOut()))
            .type(named("java.io.FileInputStream").or(named("java.io.FileOutputStream")))
            .transform((builder, type, classLoader, module) ->
                builder
                    .constructor(any())
                    .intercept(Advice.to(Interceptor.class))
            )
            .installOn(instrumentation);
    }

    private static void injectBootstrapClasses(Instrumentation instrumentation) {
        try {
            File jarFile = File.createTempFile(Agent.class.getSimpleName(), ".jar");
            jarFile.deleteOnExit();
            try (JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)))) {
                for (Class<?> bootstrapClass : BOOTSTRAP_CLASSES) {
                    String klassPath = classFileFullname(bootstrapClass);
                    jarOutputStream.putNextEntry(new JarEntry(klassPath));
                    jarOutputStream.write(readFully(bootstrapClass.getClassLoader().getResourceAsStream(klassPath)));
                }
            }
            instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(jarFile));
        } catch (IOException exception) {
            throw new IllegalStateException("Cannot write jar file to disk", exception);
        }
    }

    private static String classFileFullname(Class<?> bootstrapClass) {
        return bootstrapClass.getName().replace('.', '/') + CLASS_FILE_EXTENSION;
    }

    private static byte[] readFully(InputStream input) throws IOException {
        byte[] buffer = new byte[8192];
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            int bytesRead;
            while ((bytesRead = input.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }
            return output.toByteArray();
        }
    }

    public static class Interceptor{
        @Advice.OnMethodExit
        public static void intercept() {
            System.out.println("Exit constructor");
        }
    }

}

以上代码打印:

java.lang.IllegalStateException: Cannot call super (or default) method for public java.io.FileOutputStream(java.lang.String) throws java.io.FileNotFoundException
    at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:97)
    at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7812)
    at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7765)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:620)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:609)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor$CodePreservingMethodVisitor.visitCode(TypeWriter.java:3969)
    at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2941)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
    at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2676)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:8902)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:9303)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9266)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1300(AgentBuilder.java:9044)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9622)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9572)
    at java.security.AccessController.doPrivileged(Native Method)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9191)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRetransformation.doApply(AgentBuilder.java:6213)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector.apply(AgentBuilder.java:6071)
    at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy.apply(AgentBuilder.java:4252)
    at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:8258)
    at net.bytebuddy.agent.builder.AgentBuilder$Default$Delegator.installOn(AgentBuilder.java:9957)
    at com.github.soldierkam.agent.Agent.premain(Agent.java:46)
    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 sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
    at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)

如何解决这个问题?看起来像ByteBuddy尝试调用不存在的OutputStream(String)构造函数。我正在使用Byte Buddy 1.7.1和JVM 1.8.0_131

1 个答案:

答案 0 :(得分:0)

您需要使用Advice组件作为访问者来增强现有构造函数;目前,您正在实施一种新方法。

这应该有效:

builder.visit(Advice.to(Interceptor.class).on(isConstructor()));