当前位置: 首页 > 知识库问答 >
问题:

如何通过Byte-Buddy为类中的公共方法定义新注释

傅志用
2023-03-14

我正在尝试使用Byte-Buddy对类中的所有公共方法进行注释,并使用自定义注释进行注释。

我已经尝试使用此处注释中的代码:使用Byte Buddy在运行时添加方法注释

Java版本:1.8。该应用程序用于测试微服务。应用程序正在通过Spring Boot运行。我尝试在我的应用程序中为所有需要的方法添加注释,注释的值取决于方法名称。

            <dependency>
                <groupId>org.reflections</groupId>
                <artifactId>reflections</artifactId>
                <version>0.9.11</version>
            </dependency>
            <dependency>
                <groupId>net.bytebuddy</groupId>
                <artifactId>byte-buddy</artifactId>
                <version>1.10.1</version>
            </dependency>
            <dependency>
                <groupId>net.bytebuddy</groupId>
                <artifactId>byte-buddy-agent</artifactId>
                <version>1.10.1</version>
            </dependency>  

工作方法:


import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import io.qameta.allure.Step;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.MemberAttributeExtension;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.matcher.ElementMatchers;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

public class StackOverflowExample {

    private static final String REGEX = "some-package";

    public void configureAnnotation() {
        Reflections reflections = getReflections();
        Set<Class<?>> allClasses = reflections.getSubTypesOf(Object.class);
        allClasses.forEach(clazz -> {
            if (clazz.isAnnotationPresent(ConfigureSteps.class)) {
                List<Method> publicMethods = Arrays.stream(clazz.getDeclaredMethods())
                                                   .filter(method -> Modifier.isPublic(method.getModifiers()))
                                                   .collect(Collectors.toList());
                AnnotationDescription annotationDescription = AnnotationDescription.Builder.ofType(Step.class)
                                                                                           .define("value", "new annotation")
                                                                                           .build();
                publicMethods.forEach(method -> new ByteBuddy().redefine(clazz)
                                                               .visit(new MemberAttributeExtension.ForMethod()
                                                                              .annotateMethod(annotationDescription)
                                                                              .on(ElementMatchers.anyOf(method)))
                                                               .make());
            }
        });
    }

    private Reflections getReflections() {
        return new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
                                                         .addUrls(ClasspathHelper.forJavaClassPath())
                                                         .filterInputsBy(new FilterBuilder().include(REGEX)));
    }
}

在使用JUnit@BeforeAll注释进行所有测试之前,我调用configureAnnoection方法。方法被调用没有问题,但是我的类中带有ConfigreSteps注释的方法没有用Step注释进行注释。问题是什么?或者可能是我应该像这里的教程一样构建代理:http://bytebuddy.net/#/tutorial在这种情况下,我应该以什么方式覆盖转换方法?

更新:在链中添加加载方法;已添加

ByteBuddyAgent.install()
public class StackOverflowExample {

    private static final String REGEX = "example-path";

    public void configureAnnotation() {
        Reflections reflections = getReflections();
        Set<Class<?>> allClasses = reflections.getTypesAnnotatedWith(ConfigureSteps.class);
        ByteBuddyAgent.install();
        allClasses.forEach(clazz -> {
            List<Method> publicMethods = Arrays.stream(clazz.getDeclaredMethods())
                                               .filter(method -> Modifier.isPublic(method.getModifiers()))
                                               .collect(Collectors.toList());
            AnnotationDescription annotationDescription = AnnotationDescription.Builder.ofType(Step.class)
                                                                                       .define("value", "new annotation")
                                                                                       .build();
            publicMethods.forEach(method -> new ByteBuddy().redefine(clazz)
                                                           .visit(new MemberAttributeExtension.ForMethod()
                                                                          .annotateMethod(annotationDescription)
                                                                          .on(ElementMatchers.anyOf(method)))
                                                           .make()
                                                           .load(clazz.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent(
                                                                   ClassReloadingStrategy.Strategy.REDEFINITION)));
        });
    }

    private Reflections getReflections() {
        return new Reflections(new ConfigurationBuilder().setScanners(new TypeAnnotationsScanner(), new SubTypesScanner(false), new ResourcesScanner())
                                                         .addUrls(ClasspathHelper.forJavaClassPath())
                                                         .filterInputsBy(new FilterBuilder().include(REGEX)));
    }
}

此外,我还为代理定义了新的类,但并不真正理解是否需要它,因为我看到它不适用于加载的类,而我已经加载了一个。示例部分来自:重定义java。用ByteBuddy学习lang课程。试图在此方法上添加断点,但应用程序并没有停止

import java.lang.instrument.Instrumentation;

import net.bytebuddy.agent.builder.AgentBuilder;

public class ExampleAgent {
    public static void premain(String arguments,
                               Instrumentation instrumentation) {
        new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .installOn(instrumentation);
    }
}

现在出现了以下问题:java.lang.不支持的操作异常:类重新定义失败:试图更改模式(添加/删除字段)

共有1个答案

缪宪
2023-03-14

通过调用make(),您将生成一个类文件,然后立即将其抛出。

相反,您需要通过在类的类加载器上应用加载步骤中的重新定义策略来重新定义类。只有当您有一个可用的Instrumentation实例,您可以通过ByteBuddyAgent访问该实例时,这才可能实现。

 类似资料:
  • 有可能通过AOP(使用Spring aop、aspectj等)审核用@Service或@Repository注释的类的所有公共方法,或者我认为是类级别而不是方法级别的注释?我想有这样的东西:

  • 问题内容: 我一直在寻找“如何在运行时向方法添加注释”的答案,发现了一个名为Byte Buddy的出色工具,可以使用它,但仍然无法按需使用。我确定它必须能够从这个问题做到这一点Byte Buddy可以在运行时创建字段和方法注释吗? 上这堂课: 和此代码: 向 类 添加注释很容易。但是对于 方法而言 ,似乎不更改方法实现是不可能的。 我敢肯定,我只是做得不好,但是不幸的是,当方法 没有代码更改 而只

  • 我需要处理从带有注释的类的公共方法中抛出的所有异常。我尝试使用Spring AOP。这是我的记录器: 是我的注释。 然后,我将注释添加到我的配置类中。 首先,我尝试注释一些引发异常的方法。它工作得很好,但是我如何才能使这种工作用于用注释注释的类中的所有公共方法呢?

  • 我有一个超级班 我希望这个孩子班级 但是Xcode说 重写实例方法必须与它重写的声明一样可访问 那么,我如何覆盖超类中的公共方法是子类中的私有方法 谢谢你

  • 在我的方面方法中,我需要获取name(自定义注释的参数)的值 由用户调用的方法:

  • 问题内容: 将方法标记为程序包专用类是否有所不同? 之间和此处的可见性是否有实际差异? 问题答案: 如果该类不会被另一个更可见的子类扩展,则唯一的区别是 intent的清晰度* 。将所有方法包都声明为私有,使以后的读者更加难以确定同一包中的其他类将调用哪些方法。 作为我的设计解决方案,这没有多大意义,但是从技术上来说还是有可能的。