post-compilation removal of annotations from byte code

跟風遠走 提交于 2019-12-03 08:02:27

I recommend BCEL 6. You can also use ASM, but I hear BCEL is easier to use. Here is a quick test method for making a field final:

public static void main(String[] args) throws Exception {
    System.out.println(F.class.getField("a").getModifiers());
    JavaClass aClass = Repository.lookupClass(F.class);
    ClassGen aGen = new ClassGen(aClass);
    for (Field field : aGen.getFields()) {
        if (field.getName().equals("a")) {
            int mods = field.getModifiers();
            field.setModifiers(mods | Modifier.FINAL);
        }
    }
    final byte[] classBytes = aGen.getJavaClass().getBytes();
    ClassLoader cl = new ClassLoader(null) {
        @Override
        protected synchronized Class<?> findClass(String name) throws ClassNotFoundException {
            return defineClass("F", classBytes, 0, classBytes.length);
        }
    };
    Class<?> fWithoutDeprecated = cl.loadClass("F");
    System.out.println(fWithoutDeprecated.getField("a").getModifiers());
}

Of course, you would actually write your classes out to disk as files and then jar them up but this is easier for trying things out. I don't have BCEL 6 handy, so I can't modify this example to remove annotations, but I imagine the code would be something like:

public static void main(String[] args) throws Exception {
    ...
    ClassGen aGen = new ClassGen(aClass);
    aGen.setAttributes(cleanupAttributes(aGen.getAttributes()));
    aGen.getFields();
    for (Field field : aGen.getFields()) {
        field.setAttributes(cleanupAttributes(field.getAttributes()));
    }
    for (Method method : aGen.getMethods()) {
        method.setAttributes(cleanupAttributes(method.getAttributes()));
    }
    ...
}

private Attribute[] cleanupAttributes(Attribute[] attributes) {
    for (Attribute attribute : attributes) {
        if (attribute instanceof Annotations) {
            Annotations annotations = (Annotations) attribute;
            if (annotations.isRuntimeVisible()) {
                AnnotationEntry[] entries = annotations.getAnnotationEntries();
                List<AnnotationEntry> newEntries = new ArrayList<AnnotationEntry>();
                for (AnnotationEntry entry : entries) {
                    if (!entry.getAnnotationType().startsWith("javax")) {
                        newEntries.add(entry);
                    }
                }
                annotations.setAnnotationTable(newEntries);
            }
        }
    }
    return attributes;
}

ProGuard will also do this, in addition to obfuscating your code.

Peter Fichtner

There is an additional AntTask Purge Annotation References Ant Task, which

Purge references to annotations out of the java bytecode/classfiles (remove the @Anno tag from annotated elements). Now you can use annotations to check constellations in bytecode after compilation but remove the used annos before releasing the jars.

I have used ByteBuddy library to remove annotations. Unfortunately I was unable to remove annotation with high level api, so I used ASM api. Here is example, how you can remove @Deprecated annotation from field of a class:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.jar.asm.AnnotationVisitor;
import net.bytebuddy.jar.asm.FieldVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.jar.asm.Type;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.annotation.Annotation;
import java.util.Arrays;

public class Test {

    public static class Foo {
        @Deprecated
        public Integer bar;
    }

    public static void main(String[] args) throws Exception {
        System.out.println("Annotations before processing " + getAnnotationsString(Foo.class));
        Class<? extends Foo> modifiedClass = new ByteBuddy()
                .redefine(Foo.class)
                .visit(new AsmVisitorWrapper.ForDeclaredFields()
                        .field(ElementMatchers.isAnnotatedWith(Deprecated.class),
                                new AsmVisitorWrapper.ForDeclaredFields.FieldVisitorWrapper() {
                                    @Override
                                    public FieldVisitor wrap(TypeDescription instrumentedType,
                                                             FieldDescription.InDefinedShape fieldDescription,
                                                             FieldVisitor fieldVisitor) {
                                        return new FieldVisitor(Opcodes.ASM5, fieldVisitor) {
                                            @Override
                                            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                                                if (Type.getDescriptor(Deprecated.class).equals(desc)) {
                                                    return null;
                                                }
                                                return super.visitAnnotation(desc, visible);
                                            }
                                        };
                                    }
                                }))
                // can't use the same name, because Test$Foo is already loaded
                .name("Test$Foo1")
                .make()
                .load(Test.class.getClassLoader())
                .getLoaded();
        System.out.println("Annotations after processing " + getAnnotationsString(modifiedClass));
    }

    private static String getAnnotationsString(Class<? extends  Foo> clazz) throws NoSuchFieldException {
        Annotation[] annotations = clazz.getDeclaredField("bar").getDeclaredAnnotations();
        return Arrays.toString(annotations);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!