Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/powershell/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何删除/收缩“import some.clazz.SomeClass;”Java中字节码操作库/框架的语句?_Java_Bytecode_Java Bytecode Asm_Byte Buddy_Bcel - Fatal编程技术网

如何删除/收缩“import some.clazz.SomeClass;”Java中字节码操作库/框架的语句?

如何删除/收缩“import some.clazz.SomeClass;”Java中字节码操作库/框架的语句?,java,bytecode,java-bytecode-asm,byte-buddy,bcel,Java,Bytecode,Java Bytecode Asm,Byte Buddy,Bcel,我有以下课程: package some.clazz.client; import some.clazz.SomeClass; public class SomeClassClient { ... public SomeClass getProc(); ... } 我已经从SomeClassClient类字节码中删除/收缩/删除了这个getProc Java方法 通过使用new MemberRemove.str

我有以下课程:

    package some.clazz.client;

    import some.clazz.SomeClass;

    public class SomeClassClient {
        ...
        public SomeClass getProc();
        ...
    }
我已经从SomeClassClient类字节码中删除/收缩/删除了这个getProc Java方法 通过使用new MemberRemove.stripMethodsElementMatcher;ByteBuddy变换 在net.bytebuddy中:byte buddy maven plugin maven plugin。 但是导入一些.clazz.SomeClass;语句仍然存在并由CFR Java反编译器显示

SomeClassClient类中没有对SomeClass类的任何其他引用

我怎样才能从字节码中删除这个import语句?我假设它位于常量池中? 因为在尝试使用“SomeClassClient”类时,我仍然得到ClassNotFoundException

我的班级

public class MethodsRemover implements net.bytebuddy.build.Plugin {
    ...
    @Override
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
                                        TypeDescription typeDescription,
                                        ClassFileLocator classFileLocator) {
        try{
            return builder.visit(new MemberRemoval().stripMethods(
                ElementMatchers.any().and(
                    isAnnotatedWith(Transient.class)
                    .and(
                        t -> {
                            log.info(
                                "ByteBuddy transforming class: {}, strip method: {}",
                                typeDescription.getName(),
                                t
                            );
                            return true;
                        }
                    )
                ).or(
                    target -> Arrays.stream(STRIP_METHODS).anyMatch(
                        m -> {
                            Class<?> methodReturnType = getMethodReturnType(m);
                            String methodName = getMethodName(m);
                            Class<?>[] methodParameters = getMethodParameters(m);
                            return
                                isPublic()
                                .and(returns(
                                    isVoid(methodReturnType)
                                        ? is(TypeDescription.VOID)
                                        : isSubTypeOf(methodReturnType)
                                ))
                                .and(named(methodName))
                                .and(isNoParams(m)
                                    ? takesNoArguments()
                                    : takesArguments(methodParameters)
                                )
                                .and(t -> {
                                    log.info(
                                        "ByteBuddy transforming class: {}, strip method: {}",
                                        typeDescription.getName(),
                                        t
                                    );
                                    return true;
                                }).matches(target)
                            ;
                        }
                    )
                )
            ));
            ...
}
我添加了以下入口点,并在bytebuddy插件中将其配置为使用:

public static class EntryPoint implements net.bytebuddy.build.EntryPoint {
    private net.bytebuddy.build.EntryPoint typeStrategyEntryPoint = Default.REDEFINE;

    public EntryPoint() {
    }

    public EntryPoint(net.bytebuddy.build.EntryPoint typeStrategyEntryPoint) {
        this.typeStrategyEntryPoint = typeStrategyEntryPoint;
    }

    @Override
    public ByteBuddy byteBuddy(ClassFileVersion classFileVersion) {
        return typeStrategyEntryPoint
            .byteBuddy(classFileVersion)
            .with(ClassWriterStrategy.Default.CONSTANT_POOL_DISCARDING)
            .ignore(none()); // Traverse through all (include synthetic) methods of type
    }

    @Override
    public DynamicType.Builder<?> transform(TypeDescription typeDescription,
                                            ByteBuddy byteBuddy,
                                            ClassFileLocator classFileLocator,
                                            MethodNameTransformer methodNameTransformer) {
        return typeStrategyEntryPoint
            .transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
    }
}

为了重现您的问题,我使用了以下程序,使用了同样由Byte Buddy使用的ASM库:

ClassWriter cw=新的ClassWriter0; cw.visit52,ACC_ABSTRACT,无效,null,java/lang/Object,null; MethodVisitor mv=cw.visitMethod ACC|U抽象| ACC|U公共,测试,Lnon/EXISTIVE/Class;,空,空,; visitEnd mv; cw.visitEnd; 字节[]invalidclassBytes=cw.toByteArray; cw=新的ClassWriternew ClassReaderinvalidclassBytes,0; cw.visit52,ACC|u ABSTRACT | ACC|u INTERFACE,Test,null,java/lang/Object,null; mv=连续访问方法C|u STATIC|ACC|u PUBLIC,test,V,null,null; mv.visitFieldInsnGETSTATIC,java/lang/System,out,Ljava/io/PrintStream;; 生成类的mv.visitldcinnhello; mv.visitMethodInVokeVirtual, java/io/PrintStream、println、Ljava/lang/String;五、 虚假的; mv.visitinsreturn; mv.VisitMax2,1; visitEnd mv; cw.visitEnd; byte[]classBytes=cw.toByteArray; MethodHandles.lookup.DefineClassBytes; Class.forNameTest.getDeclaredMethodtest.invokenull; System.out.println; 路径p=Files.writeFiles.createTempFileClass、Test.class、classBytes; ToolProvider.findFirstjavap .ifPresentjavap->javap.runSystem.out,System.err,-c,-v,p.toString; Files.deletep; 试一试{ 类别cl=MethodHandles.lookup.defineClassinvalidclassBytes; System.out.PRINTLNLDEFINED+cl; cl.方法; } 捕捉错误{ System.out.printlngot预期错误+e; } 它首先为名为Invalid的类生成字节码,该类包含返回类型为non.existence.class的方法。然后,它使用类读取器生成一个类测试,该类读取器读取first的字节码作为类编写器的输入,类编写器将复制整个常量池,包括对不存在的类的引用

第二个类Test被转换为一个运行时类,并调用它的测试方法。此外,字节码被转储到一个临时文件中,javap在其上运行,以显示常量池。只有在这些步骤之后,才会尝试为Invalid创建运行时类,以引发错误

在我的机器上,它打印:

来自生成类的您好 类文件/C:/Users/███████████/AppData/Local/Temp/Class10921011438737096460Test.class 最后修改日期:2021年3月29日;大小312字节 SHA-256校验和63df4401143b4fb57b4815fc193f3e47fdd4c301cd76fa7f945edb415e14330a 接口测试 次要版本:0 主要版本:52 标志:0x0600 ACC_接口,ACC_摘要 本课程:8//Test 超级类:4//java/lang/Object 接口:0,字段:0,方法:1,属性:0 固定池: 1=Utf8无效 2=类别1//无效 3=Utf8 java/lang/Object 4=类3//java/lang/Object 5=Utf8测试 6=Utf8 Lnon/存在/类别; 7=Utf8测试 8=7级//试验 9=Utf8 V 10=Utf8 java/lang/System 11=类10//java/lang/System 12=Utf8输出 13=Utf8 Ljava/io/PrintStream; 14=名称和类型12:13//out:Ljava/io/PrintStream; 15=Fieldref 11.14//java/lang/System.out:Ljava/io/PrintStream; 16=生成类的Utf8 Hello 17=字符串16//Hello来自生成的类 18=Utf8 java/io/PrintStream 19=类18//java/io/PrintStream 20=Utf8打印LN 21=Utf8 Ljava/lang/String;v 22=NameAndType 20:21//println:Ljava/lang/String;v 23=Methodref 19.22//java/io/PrintStream.println:Ljava/lang/String;v 24=Utf8代码 { 公共静态空隙试验; 描述符:V 标志:0x0009 ACC\U PUBLIC,ACC\U STATIC 代码: 堆栈=2,局部变量=1,参数大小=0 0:getstatic 15//Field java/lang/System.out:Ljava/io/PrintStream; 3:ldc 17//从生成的字符串Hello 班 5:invokeVirtual23//方法java/io/PrintStream.println:Ljava/lang/String;v 8:返回 } 定义的类无效 获取了预期错误java.lang.NoClassDefFoundError:不存在/类 它表明第一类的方法Lnon/existing/class的签名;存在于第二个类文件中,但由于没有指向它的方法定义,它只是一个未使用的UTF-8类型条目,没有任何关于包含类型引用的提示,因此不会造成任何伤害

但它甚至表明,在广泛使用的热点JVM中,有一个真正的类条目指向尚未定义的类Invalid并不会阻止我们加载和使用类测试

更有趣的是,为Invalid定义运行时类的尝试也成功了,因为消息“definedclassinvalid”已经打印出来。它需要一个实际的操作在缺少的不存在的类上绊倒,比如cl.getMethods来引发错误

我做了另一个步骤,并将生成的字节码提供给CFR on。它产生了

/* *用CFR 0.150反编译。 */ 接口测试{ 公共静态孔隙试验{ 生成的类中的System.out.printlnholl; } } 显示常量池的那些悬空条目并没有导致import语句的生成

这一切都表明,您认为在转换的类中并没有主动使用类SomeClass的假设是错误的。必须积极使用导致异常和import语句生成的类

另外值得注意的是,在另一个方向上,编译包含其他未使用类的导入语句的源代码时,类文件中不会出现对这些类的引用

中给出的信息至关重要:

我忘了指定SomeClassClient在其层次结构中有一个超类和一个接口,接口用泛型返回类型定义了这个TProc getProc方法,它反过来扩展了AbstractSomeClass,并作为SomeClass传递给超类定义

javap显示:

插装前:SomeClass getProc 检测后:AbstractSomeClass getProc 其中,as CFR反汇编程序仅显示导入语句。 我在注释文本中添加了格式

你这里的是一个。由于原始类使用更具体的返回类型实现了该方法,因此编译器添加了一个合成方法,该方法重写AbstractSomeClass getProc方法并委托给SomeClass getProc

您删除了SomeClass getProc,但没有删除桥接方法。桥接方法是仍然具有对SomeClass的引用的代码。反编译器在处理桥接方法时遇到对SomeClass的引用,因此生成了import语句,但没有为桥接方法生成源代码,因为为实际目标方法生成源代码足以重现桥接方法,因此没有必要生成普通代码


要完全消除SomeClass引用,必须从字节码中删除这两个方法。对于普通Java代码,您可以简单地放松返回类型检查,因为Java语言不允许定义具有相同名称和参数类型的多个方法。因此,当模板的返回类型是引用类型时,您可以简单地匹配任何引用返回类型,以匹配任何重写方法及其所有桥接方法。当返回类型是模板返回类型的超级类型时,您可以添加对桥接方法标志的检查,但是,如上所述,对于普通Java代码,这是不必要的。

为了重现您的问题,我使用了以下程序,使用了同样由Byte Buddy使用的ASM库:

ClassWriter cw=新的ClassWriter0; cw.visit52,ACC_ABSTRACT,无效,null,java/lang/Object,null; MethodVisitor mv=cw.visitMethod ACC|U抽象| ACC|U公共,测试,Lnon/EXISTIVE/Class;,空,空,; visitEnd mv; cw.visitEnd; 字节[]invalidclassBytes=cw.toByteArray; cw=新的ClassWriternew ClassReaderinvalidclassBytes,0; cw.visit52,ACC|u ABSTRACT | ACC|u INTERFACE,Test,null,java/lang/Object,null; mv=连续访问方法C|u STATIC|ACC|u PUBLIC,test,V,null,null; mv.visitFieldInsnGETSTATIC,java/lang/System,out,Ljava/io/PrintStream;; 生成类的mv.visitldcinnhello; mv.visitMethodInVokeVirtual, java/io/PrintStream、println、Ljava/lang/String;五、 虚假的; mv.visitinsreturn; mv.VisitMax2,1; visitEnd mv; cw.visitEnd; byte[]classBytes=cw.toByteArray; MethodHandles.lookup.DefineClassBytes; Class.forNameTest.getDeclaredMethodtest.invokenull; System.out.println; 路径p=Files.writeFiles.createTempFileClass、Test.class、classBytes; ToolProvider.findFirstjavap .ifPresentjavap->javap.runSystem.out,System.err,-c,-v,p.toString; Files.deletep; 试一试{ 类别cl=MethodHandles.lookup.defineClassinvalidclassBytes; System.out.PRINTLNLDEFINED+cl; cl.getMethod s } 捕捉错误{ System.out.printlngot预期错误+e; } 它首先为名为Invalid的类生成字节码,该类包含返回类型为non.existence.class的方法。然后,它使用类读取器生成一个类测试,该类读取器读取first的字节码作为类编写器的输入,类编写器将复制整个常量池,包括对不存在的类的引用

第二个类Test被转换为一个运行时类,并调用它的测试方法。此外,字节码被转储到一个临时文件中,javap在其上运行,以显示常量池。只有在这些步骤之后,才会尝试为Invalid创建运行时类,以引发错误

在我的机器上,它打印:

来自生成类的您好 类文件/C:/Users/███████████/AppData/Local/Temp/Class10921011438737096460Test.class 最后修改日期:2021年3月29日;大小312字节 SHA-256校验和63df4401143b4fb57b4815fc193f3e47fdd4c301cd76fa7f945edb415e14330a 接口测试 次要版本:0 主要版本:52 标志:0x0600 ACC_接口,ACC_摘要 本课程:8//Test 超级类:4//java/lang/Object 接口:0,字段:0,方法:1,属性:0 固定池: 1=Utf8无效 2=类别1//无效 3=Utf8 java/lang/Object 4=类3//java/lang/Object 5=Utf8测试 6=Utf8 Lnon/存在/类别; 7=Utf8测试 8=7级//试验 9=Utf8 V 10=Utf8 java/lang/System 11=类10//java/lang/System 12=Utf8输出 13=Utf8 Ljava/io/PrintStream; 14=名称和类型12:13//out:Ljava/io/PrintStream; 15=Fieldref 11.14//java/lang/System.out:Ljava/io/PrintStream; 16=生成类的Utf8 Hello 17=字符串16//Hello来自生成的类 18=Utf8 java/io/PrintStream 19=类18//java/io/PrintStream 20=Utf8打印LN 21=Utf8 Ljava/lang/String;v 22=NameAndType 20:21//println:Ljava/lang/String;v 23=Methodref 19.22//java/io/PrintStream.println:Ljava/lang/String;v 24=Utf8代码 { 公共静态空隙试验; 描述符:V 标志:0x0009 ACC\U PUBLIC,ACC\U STATIC 代码: 堆栈=2,局部变量=1,参数大小=0 0:getstatic 15//Field java/lang/System.out:Ljava/io/PrintStream; 3:ldc 17//来自生成类的字符串Hello 5:invokeVirtual23//方法java/io/PrintStream.println:Ljava/lang/String;V 8:返回 } 定义的类无效 获取了预期错误java.lang.NoClassDefFoundError:不存在/类 它表明第一类的方法Lnon/existing/class的签名;存在于第二个类文件中,但由于没有指向它的方法定义,它只是一个未使用的UTF-8类型条目,没有任何关于包含类型引用的提示,因此不会造成任何伤害

但它甚至表明,在广泛使用的热点JVM中,有一个真正的类条目指向尚未定义的类Invalid并不会阻止我们加载和使用类测试

更有趣的是,为Invalid定义运行时类的尝试也成功了,因为消息“definedclassinvalid”已经打印出来。它需要一个实际的操作在缺少的不存在的类上绊倒,比如cl.getMethods来引发错误

我做了另一个步骤,并将生成的字节码提供给CFR on。它产生了

/* *用CFR 0.150反编译。 */ 接口测试{ 公共静态孔隙试验{ 生成的类中的System.out.printlnholl; } } 显示常量池的那些悬空条目并没有导致import语句的生成

这一切都表明,您认为在转换的类中并没有主动使用类SomeClass的假设是错误的。必须积极使用导致异常和import语句生成的类

另外值得注意的是,在另一个方向上,编译包含其他未使用类的导入语句的源代码时,类文件中不会出现对这些类的引用

中给出的信息至关重要:

我忘了指定SomeClassClient在其层次结构中有一个超类和一个接口,接口用泛型返回类型定义了这个TProc getProc方法,它反过来扩展了AbstractSomeClass,并作为SomeClass传递给超类定义

javap显示:

插装前:SomeClass getProc 检测后:AbstractSomeClass getProc Wh 因为CFR反汇编程序只显示导入语句。 我在注释文本中添加了格式

你这里的是一个。由于原始类使用更具体的返回类型实现了该方法,因此编译器添加了一个合成方法,该方法重写AbstractSomeClass getProc方法并委托给SomeClass getProc

您删除了SomeClass getProc,但没有删除桥接方法。桥接方法是仍然具有对SomeClass的引用的代码。反编译器在处理桥接方法时遇到对SomeClass的引用,因此生成了import语句,但没有为桥接方法生成源代码,因为为实际目标方法生成源代码足以重现桥接方法,因此没有必要生成普通代码


要完全消除SomeClass引用,必须从字节码中删除这两个方法。对于普通Java代码,您可以简单地放松返回类型检查,因为Java语言不允许定义具有相同名称和参数类型的多个方法。因此,当模板的返回类型是引用类型时,您可以简单地匹配任何引用返回类型,以匹配任何重写方法及其所有桥接方法。当返回类型是模板返回类型的超级类型时,可以添加对桥接方法标志的检查,但是,如前所述,对于普通Java代码,这是不必要的。

最终我发明了一种解决方法,允许处理合成桥方法,同时仍然使用ElementMatcher-s选择要删除的方法。。。 正如@Rafael Winterhalter作者在其评论中提到的那样:当前版本的Byte Buddy lib不使用现有的MemberRemoving类处理桥接方法。因此,只需通过以下方式将其扩展为移除/剥离方法:

package com.pany.of.yours.byte.buddy;
    
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.asm.MemberRemoval;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.ClassWriterStrategy;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static net.bytebuddy.matcher.ElementMatchers.is;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isBridge;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;

...

public class MethodsRemover implements Plugin {
    private static final Logger log = LoggerFactory.getLogger(MethodsRemover.class);

    private static final Object[][] STRIP_METHODS = {
        {SomeClass.class, "getProc", void.class} //,
        // other methods here
    };

    public MethodsRemover() {
    }

    @Override
    public boolean matches(TypeDescription typeDefinitions) {
        // return typeDefinitions.getName().equals("pkg.SomeClass");
        return typeDefinitions.isAssignableTo(SomeClassSuper.class)    }

    @Override
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
                                        TypeDescription typeDescription,
                                        ClassFileLocator classFileLocator) {
        try{
            log.info(" ByteBuddy processing type =========> {}", typeDescription);
            return builder.visit(new MemberRemovalEx().stripMethods(
                ElementMatchers.none()// <= or you can use ElementMatchers.any();
                .or(t -> {            // <= + .and(..) - as a start point instead.
                    log.debug("ByteBuddy processing      method --> {}", t);
                    return false;
                })
                .or(
                    isAnnotatedWith(Transient.class)
                    .and(t -> {
                        log.info(
                            " ByteBuddy strip transient method ++> {}",
                            t
                        );
                        return true;
                    })
                )
                .or(
                    target -> Arrays.stream(STRIP_METHODS).anyMatch(
                        m -> {
                            Class<?> methodReturnType = getMethodReturnType(m);
                            String methodName = getMethodName(m);
                            Class<?>[] methodParameters = getMethodParameters(m);
                            return
                                isPublic()
                                .and(returns(
                                    isVoid(methodReturnType)
                                        ? is(TypeDescription.VOID)
                                        : isSubTypeOf(methodReturnType)
                                ))
                                .and(named(methodName))
                                .and(isNoParams(m)
                                    ? takesNoArguments()
                                    : takesArguments(methodParameters)
                                )
                                .and(t -> {
                                    log.info(
                                        " ByteBuddy strip signature method ++> {}",
                                        t
                                    );
                                    return true;
                                }).matches(target)
                            ;
                        }
                    )
                )
            ));
        } catch (Exception e) {
            log.error("ByteBuddy error: ", e);
            throw e;
        }
    }

    ...

    public static class EntryPoint implements net.bytebuddy.build.EntryPoint {
        private net.bytebuddy.build.EntryPoint typeStrategyEntryPoint = Default.REDEFINE;

        public EntryPoint() {
        }

        public EntryPoint(net.bytebuddy.build.EntryPoint typeStrategyEntryPoint) {
            this.typeStrategyEntryPoint = typeStrategyEntryPoint;
        }

        @Override
        public ByteBuddy byteBuddy(ClassFileVersion classFileVersion) {
            return typeStrategyEntryPoint
                .byteBuddy(classFileVersion)
                .with(MethodGraph.Compiler.Default.forJVMHierarchy()) // Change hashCode/equals by including a return type
                .with(ClassWriterStrategy.Default.CONSTANT_POOL_DISCARDING) // Recreate constants pool
                .ignore(none()); // Traverse through all (include synthetic) methods of type
        }

        @Override
        public DynamicType.Builder<?> transform(TypeDescription typeDescription,
                                                ByteBuddy byteBuddy,
                                                ClassFileLocator classFileLocator,
                                                MethodNameTransformer methodNameTransformer) {
            return typeStrategyEntryPoint
                .transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
        }
    }

    private class MemberRemovalEx extends MemberRemoval {
        private final Junction<FieldDescription.InDefinedShape> fieldMatcher;
        private final Junction<MethodDescription> methodMatcher;

        public MemberRemovalEx() {
            this(ElementMatchers.none(), ElementMatchers.none());
        }

        public MemberRemovalEx(Junction<FieldDescription.InDefinedShape> fieldMatcher,
                               Junction<MethodDescription> methodMatcher) {
            super(fieldMatcher, methodMatcher);
            this.fieldMatcher = fieldMatcher;
            this.methodMatcher = methodMatcher;
        }

        @Override
        public MemberRemoval stripInvokables(ElementMatcher<? super MethodDescription> matcher) {
            return new MemberRemovalEx(this.fieldMatcher, this.methodMatcher.or(matcher));
        }

        @Override
        public ClassVisitor wrap(TypeDescription instrumentedType,
                                 ClassVisitor classVisitor,
                                 Implementation.Context implementationContext,
                                 TypePool typePool,
                                 FieldList<FieldDescription.InDefinedShape> fields,
                                 MethodList<?> methods,
                                 int writerFlags,
                                 int readerFlags) {
            MethodList<MethodDescription.InDefinedShape> typeBridgeMethods =
                instrumentedType.getDeclaredMethods().filter(isBridge());
            int bridgeMethodCount = typeBridgeMethods.size();
            if (bridgeMethodCount > 0) {
                List<MethodDescription> methodsPlusBridges = new ArrayList<>(
                    methods.size() + bridgeMethodCount
                );
                methodsPlusBridges.addAll(typeBridgeMethods);
                methodsPlusBridges.addAll(methods);
                methods = new MethodList.Explicit<>(methodsPlusBridges);
            }
            return super.wrap(
                instrumentedType,
                classVisitor,
                implementationContext,
                typePool,
                fields,
                methods,
                writerFlags,
                readerFlags
            );
        }
    }
}
下面是使用的byte buddy Maven插件配置:

<build>
    <plugins>
        <plugin>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-maven-plugin</artifactId>
            <version>${byte-buddy-maven-plugin.version}</version>
            <executions>
                <execution>
                    <id>byte.buddy.strip.methods</id>
                    <phase>process-classes</phase>
                    <goals>
                        <goal>transform</goal>
                    </goals>
                    <configuration>
                        <transformations>
                            <transformation>
                                <!-- Next plugin transformer removes @Transient annotated and some predefined methods from entities -->
                                <plugin>com.pany.of.yours.byte.buddy.MethodsRemover</plugin>
                                <!-- Optionally, specify groupId, artifactId, version of the class -->
                            </transformation>
                        </transformations>
                        <!-- Optionally, add 'initialization' block with EntryPoint class -->
                        <initialization>
                            <entryPoint>
                                com.pany.of.yours.byte.buddy.MethodsRemover$EntryPoint
                            </entryPoint>
                        </initialization>
                    </configuration>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>some.your.aux.dependency.group</groupId>
                    <artifactId>dependency-artifact</artifactId>
                    <version>${project.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

最终,我发明了一种解决方法,允许处理合成桥方法,同时仍然使用ElementMatcher-s选择要删除的方法。。。 正如@Rafael Winterhalter作者在其评论中提到的那样:当前版本的Byte Buddy lib不使用现有的MemberRemoving类处理桥接方法。因此,只需通过以下方式将其扩展为移除/剥离方法:

package com.pany.of.yours.byte.buddy;
    
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.asm.MemberRemoval;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.ClassWriterStrategy;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static net.bytebuddy.matcher.ElementMatchers.is;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isBridge;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;

...

public class MethodsRemover implements Plugin {
    private static final Logger log = LoggerFactory.getLogger(MethodsRemover.class);

    private static final Object[][] STRIP_METHODS = {
        {SomeClass.class, "getProc", void.class} //,
        // other methods here
    };

    public MethodsRemover() {
    }

    @Override
    public boolean matches(TypeDescription typeDefinitions) {
        // return typeDefinitions.getName().equals("pkg.SomeClass");
        return typeDefinitions.isAssignableTo(SomeClassSuper.class)    }

    @Override
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
                                        TypeDescription typeDescription,
                                        ClassFileLocator classFileLocator) {
        try{
            log.info(" ByteBuddy processing type =========> {}", typeDescription);
            return builder.visit(new MemberRemovalEx().stripMethods(
                ElementMatchers.none()// <= or you can use ElementMatchers.any();
                .or(t -> {            // <= + .and(..) - as a start point instead.
                    log.debug("ByteBuddy processing      method --> {}", t);
                    return false;
                })
                .or(
                    isAnnotatedWith(Transient.class)
                    .and(t -> {
                        log.info(
                            " ByteBuddy strip transient method ++> {}",
                            t
                        );
                        return true;
                    })
                )
                .or(
                    target -> Arrays.stream(STRIP_METHODS).anyMatch(
                        m -> {
                            Class<?> methodReturnType = getMethodReturnType(m);
                            String methodName = getMethodName(m);
                            Class<?>[] methodParameters = getMethodParameters(m);
                            return
                                isPublic()
                                .and(returns(
                                    isVoid(methodReturnType)
                                        ? is(TypeDescription.VOID)
                                        : isSubTypeOf(methodReturnType)
                                ))
                                .and(named(methodName))
                                .and(isNoParams(m)
                                    ? takesNoArguments()
                                    : takesArguments(methodParameters)
                                )
                                .and(t -> {
                                    log.info(
                                        " ByteBuddy strip signature method ++> {}",
                                        t
                                    );
                                    return true;
                                }).matches(target)
                            ;
                        }
                    )
                )
            ));
        } catch (Exception e) {
            log.error("ByteBuddy error: ", e);
            throw e;
        }
    }

    ...

    public static class EntryPoint implements net.bytebuddy.build.EntryPoint {
        private net.bytebuddy.build.EntryPoint typeStrategyEntryPoint = Default.REDEFINE;

        public EntryPoint() {
        }

        public EntryPoint(net.bytebuddy.build.EntryPoint typeStrategyEntryPoint) {
            this.typeStrategyEntryPoint = typeStrategyEntryPoint;
        }

        @Override
        public ByteBuddy byteBuddy(ClassFileVersion classFileVersion) {
            return typeStrategyEntryPoint
                .byteBuddy(classFileVersion)
                .with(MethodGraph.Compiler.Default.forJVMHierarchy()) // Change hashCode/equals by including a return type
                .with(ClassWriterStrategy.Default.CONSTANT_POOL_DISCARDING) // Recreate constants pool
                .ignore(none()); // Traverse through all (include synthetic) methods of type
        }

        @Override
        public DynamicType.Builder<?> transform(TypeDescription typeDescription,
                                                ByteBuddy byteBuddy,
                                                ClassFileLocator classFileLocator,
                                                MethodNameTransformer methodNameTransformer) {
            return typeStrategyEntryPoint
                .transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
        }
    }

    private class MemberRemovalEx extends MemberRemoval {
        private final Junction<FieldDescription.InDefinedShape> fieldMatcher;
        private final Junction<MethodDescription> methodMatcher;

        public MemberRemovalEx() {
            this(ElementMatchers.none(), ElementMatchers.none());
        }

        public MemberRemovalEx(Junction<FieldDescription.InDefinedShape> fieldMatcher,
                               Junction<MethodDescription> methodMatcher) {
            super(fieldMatcher, methodMatcher);
            this.fieldMatcher = fieldMatcher;
            this.methodMatcher = methodMatcher;
        }

        @Override
        public MemberRemoval stripInvokables(ElementMatcher<? super MethodDescription> matcher) {
            return new MemberRemovalEx(this.fieldMatcher, this.methodMatcher.or(matcher));
        }

        @Override
        public ClassVisitor wrap(TypeDescription instrumentedType,
                                 ClassVisitor classVisitor,
                                 Implementation.Context implementationContext,
                                 TypePool typePool,
                                 FieldList<FieldDescription.InDefinedShape> fields,
                                 MethodList<?> methods,
                                 int writerFlags,
                                 int readerFlags) {
            MethodList<MethodDescription.InDefinedShape> typeBridgeMethods =
                instrumentedType.getDeclaredMethods().filter(isBridge());
            int bridgeMethodCount = typeBridgeMethods.size();
            if (bridgeMethodCount > 0) {
                List<MethodDescription> methodsPlusBridges = new ArrayList<>(
                    methods.size() + bridgeMethodCount
                );
                methodsPlusBridges.addAll(typeBridgeMethods);
                methodsPlusBridges.addAll(methods);
                methods = new MethodList.Explicit<>(methodsPlusBridges);
            }
            return super.wrap(
                instrumentedType,
                classVisitor,
                implementationContext,
                typePool,
                fields,
                methods,
                writerFlags,
                readerFlags
            );
        }
    }
}
下面是使用的byte buddy Maven插件配置:

<build>
    <plugins>
        <plugin>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-maven-plugin</artifactId>
            <version>${byte-buddy-maven-plugin.version}</version>
            <executions>
                <execution>
                    <id>byte.buddy.strip.methods</id>
                    <phase>process-classes</phase>
                    <goals>
                        <goal>transform</goal>
                    </goals>
                    <configuration>
                        <transformations>
                            <transformation>
                                <!-- Next plugin transformer removes @Transient annotated and some predefined methods from entities -->
                                <plugin>com.pany.of.yours.byte.buddy.MethodsRemover</plugin>
                                <!-- Optionally, specify groupId, artifactId, version of the class -->
                            </transformation>
                        </transformations>
                        <!-- Optionally, add 'initialization' block with EntryPoint class -->
                        <initialization>
                            <entryPoint>
                                com.pany.of.yours.byte.buddy.MethodsRemover$EntryPoint
                            </entryPoint>
                        </initialization>
                    </configuration>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>some.your.aux.dependency.group</groupId>
                    <artifactId>dependency-artifact</artifactId>
                    <version>${project.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

尝试使用ApacheBcelthat的JVM依赖,但通常情况下,常量池中未使用的类引用不会导致任何异常或错误。由于字节码中没有import语句,这取决于反编译器是否重新生成一些语句。但更可能的是,代码中实际使用的类导致了这两种情况。如何使用Byte Buddy设置此删除?如果要剥离此数据,可能需要重新计算常量池。使用new ByteBuddy.withClassWriterStrategy.Default.CONSTANT\u POOL\u DISCARDING.Linkage错误将导致NoClassDefFoundError。ClassNotFoundException通常是手动loadClass…或Class.forName…调用的标志。在这种情况下,查看堆栈跟踪会很有帮助。它不必是包含SomeClass用法的SomeClassClient类;它可以是某个ClassClient使用的任何其他类。现在,这是有价值的信息。请参阅我更新的答案的结尾。请尝试使用Apache BCELThat的JVM依赖项,但通常情况下,常量池中未使用的类引用不会导致任何异常或错误。由于字节码中没有import语句,这取决于反编译器是否重新生成一些语句。但更可能的是,代码中实际使用的类导致了这两种情况。如何使用Byte Buddy设置此删除?如果要剥离此数据,可能需要重新计算常量池。使用new ByteBuddy.withClassWriterStrategy.Default.CONSTANT\u POOL\u DISCARDING.Linkage错误将导致NoClassDefFoundError。ClassNotFoundException通常是手动loadClass…或Class.forName…调用的标志。在这种情况下,查看堆栈跟踪会很有帮助。它不必是包含SomeClass用法的SomeClassClient类;它可以是某个ClassClient使用的任何其他类。现在,这是有价值的信息。请参阅我更新的答案的结尾。非常感谢您的详细解释,但是ByteBuddy似乎根本没有访问桥接方法,因为假设那里应该有额外的配置,所以我的MethodsRever没有机会在stripMethods中应用其ElementMatchers:for it。试图找出必要的配置选项…MemberRemoving不处理桥接方法是正确的。您可以使用ASM并注册ClassVisitorWrapper。然后,您可以为要删除的所有方法返回null,这与成员删除具有相同的效果,但也将处理桥接方法。T 非常感谢您的详细解释,但是ByteBuddy似乎根本没有访问桥接方法,因为假设那里应该有额外的配置,所以我的MethodsRever没有机会在stripMethods中应用它的ElementMatchers:for it。试图找出必要的配置选项…MemberRemoving不处理桥接方法是正确的。您可以使用ASM并注册ClassVisitorWrapper。然后,您可以为要删除的所有方法返回null,这与成员删除具有相同的效果,但也将处理桥接方法。