字节好友抛出 java.lang.ClassNotFoundException:javax.servlet.http.HttpServlet
byte-buddy throws java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
我正在尝试使用基于 Byte Buddy 的 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);
我的 Advice 实现如下所示:
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...>
}
}
}
奇怪的是,当我匹配特定的 Tomcat 实现时 class:
.type(ElementMatchers.named("org.glassfish.jersey.servlet.ServletContainer"))
...然后代理设置工作(尽管 Advice 方法中对 HttpServletRequest 和 HttpServletResponse 的引用在检测过程中触发 ClassNotFoundException)。
这是否意味着我必须在代理 .jar 文件本身中包含所有代理依赖项(包括 Java Servlet API)?
我正在使用 ByteBuddy 1.6.2、Apache Tomcat v8.0.
编辑
修改后的工作代码(在 Rafael 的帮助下)如下所示:
.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 用于解析建议的 class 加载程序无法使用 servlet-family 类型。为了克服这个问题,Byte Buddy 提供了一个特定的转换器来解决用户和代理的 class 加载程序的建议 class:
new AgentBuilder.Transformation.ForAdvice()
.include(getClass().getClassLoader())
.advice("your.pkg.ServletAdvice")
至于元素匹配器,匹配参数的名称然后使用类型常量。
我正在尝试使用基于 Byte Buddy 的 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);
我的 Advice 实现如下所示:
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...>
}
}
}
奇怪的是,当我匹配特定的 Tomcat 实现时 class:
.type(ElementMatchers.named("org.glassfish.jersey.servlet.ServletContainer"))
...然后代理设置工作(尽管 Advice 方法中对 HttpServletRequest 和 HttpServletResponse 的引用在检测过程中触发 ClassNotFoundException)。
这是否意味着我必须在代理 .jar 文件本身中包含所有代理依赖项(包括 Java Servlet API)?
我正在使用 ByteBuddy 1.6.2、Apache Tomcat v8.0.
编辑 修改后的工作代码(在 Rafael 的帮助下)如下所示:
.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 用于解析建议的 class 加载程序无法使用 servlet-family 类型。为了克服这个问题,Byte Buddy 提供了一个特定的转换器来解决用户和代理的 class 加载程序的建议 class:
new AgentBuilder.Transformation.ForAdvice()
.include(getClass().getClassLoader())
.advice("your.pkg.ServletAdvice")
至于元素匹配器,匹配参数的名称然后使用类型常量。