Modify java.util class using byte-buddy-agent

白昼怎懂夜的黑 提交于 2021-01-28 15:02:56

问题


Is it possible to add a field in java.util class using byte-buddy?

I am trying to add a field in java.util.concurrent.FutureTask and intercept constructor and an arbitrary method to set and get the field value. In short, I am trying to add a field to FutureTask so that I can pass some value to child thread from parent even if they are running in a thread pool. isn't it possible to add a field in FutureTask?

FutureTaskTransofrmer

@Override
    protected ElementMatcher.Junction<TypeDescription> getNarrowTypesMatcher() {
        return named("java.util.concurrent.FutureTask");
    }

    @Override
    public AgentBuilder.Transformer getTransformer() {

        return (builder, typeDescription, classLoader, module) -> {
            beforeTransformation(typeDescription, classLoader);

            return builder
                    .defineField("pit", String.class)
                    .constructor(ElementMatchers.any())
                    .intercept(Advice.to(SetPitAdvice.class))
                    .method(named("run"))
                    .intercept(Advice.to(GetPitAdvice.class))
                    ;
        };

    }

GetPitAdvice

    @Advice.OnMethodEnter
    public static void getValues(@Advice.FieldValue(value = "pit") String pit)
            throws Exception {

        logger.info("pit in future Task {}", pit);
    }

SetPitAdvice

@Advice.OnMethodExit
    public static void setValues(
            @Advice.FieldValue(value = "pit", readOnly = false) String pit
    )
            throws Exception {

        logger.debug("Setting pit field in FutureTask");

        pit = SimulationRequestContextHolder.get("pit");

    }

AgentBuilder

    private static AgentBuilder createAgentBuilder(AutoEvictingCachingBinaryLocator binaryLocator) {
        final ByteBuddy byteBuddy = new ByteBuddy()
                .with(TypeValidation.of(AgentProperties.PROPERTIES.isDebugInstrumentation()))
                .with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE);

        return new AgentBuilder.Default(byteBuddy)
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(getListener())
                .with(binaryLocator)
                .ignore(any(), ClassLoaderNameMatcher.isReflectionClassLoader())
                .or(any(), ClassLoaderNameMatcher.classLoaderWithName("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader"))
                .or(any(), new IsIgnoredClassLoaderElementMatcher())
                .or(nameStartsWith("org.aspectj.")
                        .or(nameStartsWith("org.groovy."))
                        .or(nameStartsWith("com.sun."))
                        .or(nameStartsWith("com.p6spy."))
                        .or(nameStartsWith("net.bytebuddy."))
                        .or(nameStartsWith("org.slf4j.").and(not(nameStartsWith("org.slf4j.impl."))))
                        .or(nameContains("javassist"))
                        .or(nameContains(".asm."))
                        .or(nameStartsWith("com.java.agent.sims")
                        ))
//                .disableClassFormatChanges()
                .enableUnsafeBootstrapInjection()
                ;
    }

onTransformListener shows the class is transformed

11:27:24.141 [main] INFO com.java.agent.sims.ApplicationClassLoaderMatcher - Instrumenting ClassLoader null: true
11:27:24.186 [main] DEBUG com.java.agent.sims.transformers.ByteBuddyTransformer - TRANSFORM java.util.concurrent.FutureTask (FutureTaskTransformer)
11:27:24.466 [main] INFO com.java.agent.sims.instrument.TransformListener - Class modified by Byte Buddy: java.util.concurrent.FutureTask

but none of my advice intercepts are getting called.


回答1:


If the class is already loaded, this is not possible. Instead, you can inject a class into the bootstrap class loader using the Instrumentation API and have a static map with weak keys stored in this class where you place the field value of each instance.

Also, note that the until classes are loaded by the bootstrap class loader that cannot see any classes contained by your agent which is loaded by the system class loader. Any API used by advice must be injected into the bootstrap loader for this reason.



来源:https://stackoverflow.com/questions/56641621/modify-java-util-class-using-byte-buddy-agent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!