从 AspectJ 迁移到 Byte Buddy 插件的问题
Problem migrating from AspectJ to Byte Buddy Plugin
我正在尝试将 AspectJ 项目迁移到 Byte Buddy 插件,但遇到了一些困难。我想做编译时字节码修改。
我得到的异常是:
[ERROR] Failed to execute goal net.bytebuddy:byte-buddy-maven-plugin:1.11.0:transform (default) on project timing-example: Failed to transform class files in /tmp/timing-example/target/classes: protected void com.walterjwhite.examples.timing.TimingExampleCommandLineHandler.doRun(java.lang.String[]) does not define an index 1 -> [Help 1]
插件:
注意:
package com.walterjwhite.timing.plugin;
import static net.bytebuddy.matcher.ElementMatchers.*;
import com.walterjwhite.timing.annotation.Timing;
import java.io.IOException;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
@HashCodeAndEqualsPlugin.Enhance
public class TimingPlugin extends Plugin.ForElementMatcher implements Plugin.Factory {
public TimeoutPlugin() {
super(declaresMethod(isAnnotatedWith(Timing.class)));
}
@Override
public DynamicType.Builder<?> apply(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator) {
System.out.println("Timing: start");
for (MethodDescription.InDefinedShape methodDescription :
typeDescription
.getDeclaredMethods()
.filter(
not(isBridge()).<MethodDescription>and(isAnnotatedWith(Timing.class)))) {
System.out.println("Timing: " + methodDescription);
if (methodDescription.isAbstract()) {
throw new IllegalStateException(
"Cannot implement timing on an abstract method: " + methodDescription);
}
builder = builder.visit(Advice.to(TimingAdvice.class).on(is(methodDescription)));
}
System.out.println("Timing: end");
return builder;
}
建议:
注意:我想将原始方法调用包装在 try-catch-finally 中,这样我就可以计时了。我不确定我能否通过建议做到这一点。无论如何,那是更远的路,我想看到我可以编写一个插件并检测我的代码。
package com.walterjwhite.timing.plugin;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
public class TimingAdvice {
@RuntimeType
@Advice.OnMethodEnter
public static void onEnter(@Advice.This Object intercepted, @Origin Method method, @RuntimeType @AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
}
建议的方法:
@Timing
@Override
protected void doRun(String... arguments) {
int i = 0;
while (true) {
try {
System.out.println("i:" + i++);
Thread.sleep(50);
} catch (InterruptedException e) {
System.out.println("Exiting as instructed to do so.");
System.exit(1);
}
}
}
摘自pom.xml:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
最后,插件项目有文件,因为字节伙伴插件确实选择了它。但是,每当它尝试 t运行sform class 文件时,它都会失败。所以,我的配置不太对。
编辑#2:
pom 部分正确,我 运行 关注的另一个问题是:
NoClassDefFoundError
这是因为我需要的依赖项也被列为项目的依赖项,而不仅仅是插件。即:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins
</build>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
...
您正在混合 MethodDelegation
API 和 Advice
API 的注释。注释非常相似,因为它们打算支持相同的方法,但不幸的是它们会混淆。而不是导入
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
您需要使用在 Advice
class 中声明的同名注解。理想情况下,您为所有注释添加前缀 Advice
:
@Advice.OnMethodEnter
public static void onEnter(@Advice.This Object intercepted, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
请注意 @RuntimeType
在 Advice
API 中没有对应项。通常不需要。
我正在尝试将 AspectJ 项目迁移到 Byte Buddy 插件,但遇到了一些困难。我想做编译时字节码修改。
我得到的异常是:
[ERROR] Failed to execute goal net.bytebuddy:byte-buddy-maven-plugin:1.11.0:transform (default) on project timing-example: Failed to transform class files in /tmp/timing-example/target/classes: protected void com.walterjwhite.examples.timing.TimingExampleCommandLineHandler.doRun(java.lang.String[]) does not define an index 1 -> [Help 1]
插件: 注意:
package com.walterjwhite.timing.plugin;
import static net.bytebuddy.matcher.ElementMatchers.*;
import com.walterjwhite.timing.annotation.Timing;
import java.io.IOException;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
@HashCodeAndEqualsPlugin.Enhance
public class TimingPlugin extends Plugin.ForElementMatcher implements Plugin.Factory {
public TimeoutPlugin() {
super(declaresMethod(isAnnotatedWith(Timing.class)));
}
@Override
public DynamicType.Builder<?> apply(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator) {
System.out.println("Timing: start");
for (MethodDescription.InDefinedShape methodDescription :
typeDescription
.getDeclaredMethods()
.filter(
not(isBridge()).<MethodDescription>and(isAnnotatedWith(Timing.class)))) {
System.out.println("Timing: " + methodDescription);
if (methodDescription.isAbstract()) {
throw new IllegalStateException(
"Cannot implement timing on an abstract method: " + methodDescription);
}
builder = builder.visit(Advice.to(TimingAdvice.class).on(is(methodDescription)));
}
System.out.println("Timing: end");
return builder;
}
建议: 注意:我想将原始方法调用包装在 try-catch-finally 中,这样我就可以计时了。我不确定我能否通过建议做到这一点。无论如何,那是更远的路,我想看到我可以编写一个插件并检测我的代码。
package com.walterjwhite.timing.plugin;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
public class TimingAdvice {
@RuntimeType
@Advice.OnMethodEnter
public static void onEnter(@Advice.This Object intercepted, @Origin Method method, @RuntimeType @AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
}
建议的方法:
@Timing
@Override
protected void doRun(String... arguments) {
int i = 0;
while (true) {
try {
System.out.println("i:" + i++);
Thread.sleep(50);
} catch (InterruptedException e) {
System.out.println("Exiting as instructed to do so.");
System.exit(1);
}
}
}
摘自pom.xml:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
最后,插件项目有文件,因为字节伙伴插件确实选择了它。但是,每当它尝试 t运行sform class 文件时,它都会失败。所以,我的配置不太对。
编辑#2:
pom 部分正确,我 运行 关注的另一个问题是: NoClassDefFoundError
这是因为我需要的依赖项也被列为项目的依赖项,而不仅仅是插件。即:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins
</build>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
...
您正在混合 MethodDelegation
API 和 Advice
API 的注释。注释非常相似,因为它们打算支持相同的方法,但不幸的是它们会混淆。而不是导入
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
您需要使用在 Advice
class 中声明的同名注解。理想情况下,您为所有注释添加前缀 Advice
:
@Advice.OnMethodEnter
public static void onEnter(@Advice.This Object intercepted, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
请注意 @RuntimeType
在 Advice
API 中没有对应项。通常不需要。