从 AspectJ 迁移到 Byte Buddy 插件的问题

问题描述

我正在尝试将 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>

最后,插件项目已经有了文件,因为字节伙伴插件确实把它捡起来了。但是,每当它尝试转换类文件时,它都会失败。所以,我的配置不太对。

编辑#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 类中声明的同名注解。理想情况下,您在所有注释前都使用 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());
}

请注意,@RuntimeTypeAdvice API 中没有等效项。通常不需要。