byte buddy抛出java.lang.ClassNotFoundException:javax.servlet.http.HttpServlet

byte buddy抛出java.lang.ClassNotFoundException:javax.servlet.http.HttpServlet,java,byte-buddy,Java,Byte Buddy,我试图使用基于字节的Java代理来插入对javax.servlet.http.HttpServlet的service()方法的调用。我的代码中的premain函数被正确调用,但检测失败,堆栈跟踪: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAcces

我试图使用基于字节的Java代理来插入对javax.servlet.http.HttpServlet的service()方法的调用。我的代码中的premain函数被正确调用,但检测失败,堆栈跟踪:

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     sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
  at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
Caused by: java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet
    at ub.jagent.ServletInstrumentation.instrument(ServletInstrumentation.java:24)
    at ub.jagent.StackTracerAgent.premain(StackTracerAgent.java:75)
    ... 6 more
Caused by: java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 8 more
FATAL ERROR in native method: processing of -javaagent failed
我使用代理生成器如下所示:

AgentBuilder b = new AgentBuilder.Default().ignore(ElementMatchers.none())
        .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
        .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
        .with(AgentBuilder.TypeStrategy.Default.REDEFINE)   
        .type(ElementMatchers.isSubTypeOf(HttpServlet.class))
            .transform((builder, type, classLoader) -> 
                builder.visit(Advice.to(ServletAdvice.class) 
                .on(ElementMatchers.named("service")
                .and(ElementMatchers.takesArgument(0, HttpServletRequest.class))
                .and(ElementMatchers.takesArgument(1, HttpServletResponse.class)))).installOn(inst);
我的建议如下:

public class ServletAdvice {

    @Advice.OnMethodEnter
    public static void before(@Advice.This Object obj,
                               @Advice.Argument(value = 0) HttpServletRequest a1,
                               @Advice.Argument(value = 1) HttpServletResponse a2,
                               @Advice.Origin Method method) 
        {
            System.out.println("\nRunning pre-method logic for " + obj.getClass().getCanonicalName() + ":" + method.getName() + "()");
        <...etc...>
        }
    }
}
         .type(ElementMatchers.isSubTypeOf(new TypeDescription.Latent("javax.servlet.GenericServlet", Modifier.PUBLIC | Modifier.ABSTRACT, TypeDescription.Generic.OBJECT, null)))
         .transform(
                 new AgentBuilder.Transformer.ForAdvice()
                    .include(getClass().getClassLoader())
                    .advice(
                        ElementMatchers.named("service")
                            .and(ElementMatchers.takesArgument(0, ElementMatchers.named("javax.servlet.http.HttpServletRequest"))
                            .and(ElementMatchers.takesArgument(1, ElementMatchers.named( "javax.servlet.http.HttpServletResponse")),
                        ServletAdvice.class.getName())
         );
…然后代理安装程序开始工作(尽管Advice方法中对HttpServletRequest和HttpServletResponse的引用在检测过程中触发ClassNotFoundException)

这是否意味着我必须在agent.jar文件本身中包含所有代理依赖项(包括JavaServletAPI)

我使用的是ByteBuddy 1.6.2,ApacheTomcatV8.0

编辑 修订后的工作代码(在Rafael的帮助下)如下所示:

public class ServletAdvice {

    @Advice.OnMethodEnter
    public static void before(@Advice.This Object obj,
                               @Advice.Argument(value = 0) HttpServletRequest a1,
                               @Advice.Argument(value = 1) HttpServletResponse a2,
                               @Advice.Origin Method method) 
        {
            System.out.println("\nRunning pre-method logic for " + obj.getClass().getCanonicalName() + ":" + method.getName() + "()");
        <...etc...>
        }
    }
}
         .type(ElementMatchers.isSubTypeOf(new TypeDescription.Latent("javax.servlet.GenericServlet", Modifier.PUBLIC | Modifier.ABSTRACT, TypeDescription.Generic.OBJECT, null)))
         .transform(
                 new AgentBuilder.Transformer.ForAdvice()
                    .include(getClass().getClassLoader())
                    .advice(
                        ElementMatchers.named("service")
                            .and(ElementMatchers.takesArgument(0, ElementMatchers.named("javax.servlet.http.HttpServletRequest"))
                            .and(ElementMatchers.takesArgument(1, ElementMatchers.named( "javax.servlet.http.HttpServletResponse")),
                        ServletAdvice.class.getName())
         );

我假设Byte Buddy用于解析通知的类加载器无法使用servlet类型族。为了克服这一问题,Byte Buddy提供了一个特定的转换器,用于解析用户和代理的类加载器的通知类:

new AgentBuilder.Transformation.ForAdvice()
  .include(getClass().getClassLoader())
  .advice("your.pkg.ServletAdvice")

至于元素匹配器,与其匹配参数的名称,不如使用类型常量。

我改进了匹配参数的方式。目前,只有这样的匹配器可用于泛型类型参数,您需要将
erausre
添加到参数匹配器中,但这在1.6.7中得到了修复。这种方法可以用于常规拦截器吗?或者它只适用于
通知
?拦截器不是内联的,因此它们不显示这些特性。