Post-compilation removing annotations from byte code

we use a library containing beans annotated with JAXB annotations. nothing about how we use these classes depends on JAXB. in other words, we do not need JAXB and are not dependent on annotations.

however, since annotations exist, they ultimately refer to other classes that process annotations. this requires me to bundle JAXB in our application, which is unacceptable because JAXB is in the javax.* package (the android does not allow you to include "core libraries" in your application).

therefore, with that in mind, I'm looking for a way to remove annotations from compiled byte code. I know that there are utilities for manipulating byte code, but this is completely new to me. any help started for this purpose will be appreciated.

+8
java android annotations javassist bcel
source share
4 answers

I recommend BCEL 6. You can also use ASM, but I heard BCEL is easier to use. Below is a quick method for creating the final field:

 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 write your classes to disk as files, and then jar them, but it's easier to try. I don't have BCEL 6 support, so I can't change this example to remove annotations, but I think the code would be something like this:

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

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

+1
source share

There is an additional AntTask Links to clean up Ant annotations A task that

Clear annotation links from java bytecode / classes (remove @Anno tag from annotated elements). Now you can use annotations to check for constellations in bytecode after compilation, but delete the used annos before releasing jars.

+1
source share

I used ByteBuddy to remove annotations. Unfortunately, I could not remove the annotation from the high-level api, so I used the ASM api. Here is an example of how you can remove @Deprecated annotation from a class field:

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

All Articles