2012-06-18 8 views
8

stiamo usando una libreria che contiene bean annotati con annotazioni JAXB. nulla nel modo in cui usiamo queste classi dipende da JAXB. in altre parole, non abbiamo bisogno di JAXB e non dipendiamo dalle annotazioni.rimozione post-compilazione di annotazioni dal byte code

tuttavia, poiché le annotazioni esistono, finiscono per essere referenziate da altre classi che elaborano le annotazioni. ciò mi impone di raggruppare JAXB nella nostra applicazione, che non è consentita, perché JAXB si trova nel pacchetto javax.* (Android non consente l'inclusione di "librerie di base" nell'applicazione).

quindi, con questo in mente, sto cercando un modo per rimuovere le annotazioni dal codice byte compilato. so che ci sono utility per manipolare il codice byte, ma questo è abbastanza nuovo per me. qualsiasi aiuto per iniziare a tal fine sarebbe apprezzato.

+0

Quali altre classi fanno riferimento ad esse? Perché hai quelle lezioni incluse nella tua app per Android? –

+0

non ho il controllo sulle classi il riferimento alle annotazioni purtroppo. sono referenziati da un'altra libreria. –

+0

Sono ancora curioso, qual è la libreria che fa riferimento a queste classi? –

risposta

2

Raccomando BCEL 6. È anche possibile utilizzare ASM, ma ho sentito che BCEL è più facile da usare. Ecco un metodo di test rapido per fare un campo finale:

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

Naturalmente, si sarebbe in realtà scrivere le classi su disco come file e poi li vaso up, ma questo è più facile per provare le cose. Non ho BCEL 6 a portata di mano, quindi non posso modificare questo esempio per rimuovere le annotazioni, ma immagino il codice sarebbe qualcosa di simile:

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

Per chiunque tenti di farlo, dovevo anche modificare "ConstantPool" (conteneva l'annotazione anche dopo averlo rimosso dagli attributi). Potrebbe essere che ho fatto qualcosa di sbagliato, ma ora funziona per me. – Mayjak

1

ProGuard farà anche questo, oltre ad offuscare il codice .

+0

grazie. sì, ma far progedere di proguard contro un insieme ampio e vario di dipendenze è un progetto in sé. stavo cercando una soluzione più rapida (se esistesse). –

+0

Come posso fare questo con ProGuard? Vedo un'opzione '-keepattributes' che può essere utilizzata per le annotazioni, ma non vedo molto sulla rimozione. – theblang

1

V'è un ulteriore AntTask Purge Annotation References Ant Task, che

Purge riferimenti ad annotazioni fuori del bytecode Java/classfiles (rimuovere il tag @Anno da elementi annotati). Ora è possibile utilizzare le annotazioni per controllare le costellazioni in bytecode dopo la compilazione ma rimuovere gli annus utilizzati prima di rilasciare i vasi.

0

Ho usato la libreria ByteBuddy per rimuovere le annotazioni. Purtroppo non sono riuscito a rimuovere le annotazioni con l'API di livello elevato, quindi ho usato l'API ASM. Ecco un esempio di come rimuovere @Deprecated annotazione dal campo di una classe:

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