post-compilation removal of annotations from byte code

有些话、适合烂在心里 提交于 2019-12-04 12:09:51

问题


we are using a library that contains beans that are annotated with JAXB annotations. nothing in the way we use these classes depends on JAXB. in other words, we don't need JAXB and do not depend on the annotations.

however, because the annotations exist, they end up being referenced by other classes that process annotations. this requires me to bundle JAXB in our application, which isn't allowed, because JAXB is in the javax.* package (android doesn't allow "core libraries" to be included in your application).

so, with this in mind, i'm looking for a way to remove the annotations from the compiled byte code. i know there are utilities for manipulating byte code, but this is quite new to me. any help getting started toward this end would be appreciated.


回答1:


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



回答2:


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




回答3:


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.




回答4:


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


来源:https://stackoverflow.com/questions/11092573/post-compilation-removal-of-annotations-from-byte-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!