本文介绍了如何删除/缩小“导入some.clazz.SomeClass;"Java中通过字节码操作库/框架进行声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下课程:

    package some.clazz.client;

    import some.clazz.SomeClass;

    public class SomeClassClient {
        ...
        public SomeClass getProc();
        ...
    }

我已经从 SomeClassClient 类字节码中删除/缩小/删除了此 getProc() Java方法通过使用 new MemberRemoval().stripMethods(ElementMatcher); ByteBuddy转换在 net.bytebuddy:byte-buddy-maven-plugin Maven插件中.但是 import some.clazz.SomeClass; 语句仍然存在,并由 CFR Java Decompiler 显示!

I've removed/shrunk/deleted this getProc() Java method from SomeClassClient class bytecodeby using new MemberRemoval().stripMethods(ElementMatcher); ByteBuddy transformationin net.bytebuddy:byte-buddy-maven-plugin Maven Plugin.But import some.clazz.SomeClass; statement is still present and shown by CFR Java Decompiler!

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

There are no any another reference to SomeClass class in SomeClassClient class.

如何从字节码中删除此导入语句(真的,我假设它位于常量池中)?因为尝试使用"SomeClassClient"类时仍会收到ClassNotFoundException.

How can I remove this import statement from bytecode (really I'm assuming it's located in constant pool)?Because I'm still getting ClassNotFoundException when trying to use 'SomeClassClient' class.

我的课

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)
                            ;
                        }
                    )
                )
            ));
            ...
}

我添加了以下EntryPoint并在bytebuddy插件中对其进行了配置以供使用:

I've added the following EntryPoint and configured it in bytebuddy plugin to use:

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);
    }
}

推荐答案

最终,我发明了一种变通方法,该方法可处理合成桥方法,同时仍使用ElementMatcher-s选择要删除的方法..如上面@Rafael Winterhalter(作者)在其评论中所述:当前(当前为v1.10.22)版本的Byte-Buddy lib无法使用其现有的MemberRemoval类处理桥接方法.因此,只需按以下方式将其扩展为删除/剥离方法即可:

Eventually I've invented a workaround that allows to handle the synthetic bridge methods and at the same time still to use ElementMatcher-s to select methods to remove...As mentioned @Rafael Winterhalter (author) above in its comment: Byte-Buddy lib at its current (v1.10.22 at the moment) version does not handle bridge methods by using its existing MemberRemoval class. So just extend it to remove/strip methods in the following manner:

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
            );
        }
    }
}

这也是使用的字节码Maven插件配置:

And also here is the used byte-buddy Maven plugin configuration:

<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>

这篇关于如何删除/缩小“导入some.clazz.SomeClass;"Java中通过字节码操作库/框架进行声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 14:23
查看更多