post-compilation removal of annotations from byte code

前端 未结 4 2170
太阳男子
太阳男子 2021-02-08 15:50

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

相关标签:
4条回答
  • 2021-02-08 16:16

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

    0 讨论(0)
  • 2021-02-08 16:22

    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);
        }
    }
    
    0 讨论(0)
  • 2021-02-08 16:23

    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;
    }
    
    0 讨论(0)
  • 2021-02-08 16:24

    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.

    0 讨论(0)
提交回复
热议问题