2012-06-18 7 views
8

estamos utilizando una biblioteca que contiene beans que están anotados con anotaciones JAXB. nada en la forma en que usamos estas clases depende de JAXB. en otras palabras, no necesitamos JAXB y no dependemos de las anotaciones.eliminación posterior a la compilación de las anotaciones del código de bytes

sin embargo, debido a que las anotaciones existen, terminan siendo referenciadas por otras clases que procesan anotaciones. esto requiere que agrupe JAXB en nuestra aplicación, lo que no está permitido, porque JAXB está en el paquete javax.* (Android no permite incluir "bibliotecas principales" en su aplicación).

por lo tanto, con esto en mente, estoy buscando una forma de eliminar las anotaciones del código de bytes compilado. Sé que hay utilidades para manipular código de bytes, pero esto es bastante nuevo para mí. cualquier ayuda para comenzar hacia este fin sería apreciada.

+0

¿Qué otras clases los referencian? ¿Por qué tienes esas clases incluidas en tu aplicación de Android? –

+0

no tengo control sobre las clases, la referencia de las anotaciones desafortunadamente. se les hace referencia desde otra biblioteca. –

+0

Todavía tengo curiosidad, ¿cuál es la biblioteca que hace referencia a estas clases? –

Respuesta

2

Recomiendo BCEL 6. También puede usar ASM, pero escucho que BCEL es más fácil de usar. Aquí es un método de prueba rápida para hacer un campo 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()); 
} 

Por supuesto, usted realmente escribir sus clases a cabo en el disco como archivos y luego sacudirlos hasta, pero esto es más fácil de probar cosas. No tengo BCEL 6 a mano, así que no puedo modificar este ejemplo para eliminar las anotaciones, pero me imagino que el código sería algo como:

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

Para cualquiera que intente hacer esto, también tuve que modificar el "ConstantPool" (contenía la anotación incluso después de eliminarlo de los atributos). Podría ser que hice algo mal, pero ahora funciona para mí. – Mayjak

1

ProGuard también lo hará, además de ofuscar su código .

+0

gracias. sí, pero lograr que se corra en contra de un amplio y variado conjunto de dependencias es un proyecto en sí mismo. estaba buscando una solución más rápida (si existía). –

+0

¿Cómo puedo hacer esto con ProGuard? Veo una opción '-keepattributes' que se puede usar para anotaciones, pero no se ve mucho sobre la eliminación. – theblang

1

Hay un AntTask adicional Purge Annotation References Ant Task, que

referencias de purga para anotaciones fuera del código de bytes de Java/archivos de clase (quitar la etiqueta @Anno a partir de elementos anotados). Ahora puede usar las anotaciones para verificar las constelaciones en el código de bytes después de la compilación, pero eliminar los annos utilizados antes de soltar los frascos.

0

He usado ByteBuddy biblioteca para eliminar anotaciones. Lamentablemente, no pude eliminar la anotación con API de alto nivel, así que utilicé la API de ASM. Aquí hay un ejemplo de cómo puede eliminar la anotación @Deprecated del campo de una clase:

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