2014-07-02 19 views
5

Mam do czynienia z problemem, w którym muszę zmodyfikować informacje o pakiecie.Nie można zmienić adnotacji pakietu-info.java przy użyciu języka Java 8

package-info.java

@javax.xml.bind.annotation.XmlSchema(namespace = "http://some.url/soap/style/document_literal") 
package org.example.wsdl.wsdl; 

Poniższy kod działa poprawnie z 1.7.0_45.

//   do not load any classes before, this could break the following code. 
      Class<?> pkgInfo = Class.forName("org.example.wsdl.package-info", true, NameSpaceModifier.class.getClassLoader()); 
      Field field = Class.class.getDeclaredField("annotations"); 
      field.setAccessible(true); 

      final XmlSchema oldAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0]; 
      logger.debug("Old Annotation namespace value was: " + oldAnnotation.namespace()); 
      XmlSchema newAnnotation = new XmlSchema() { 

       @Override 
       public XmlNs[] xmlns() { 
        return oldAnnotation.xmlns(); 
       } 

       @Override 
       public String namespace() { 
        return "newNs"; 
       } 

       @Override 
       public XmlNsForm elementFormDefault() { 
        return oldAnnotation.elementFormDefault(); 
       } 

       @Override 
       public XmlNsForm attributeFormDefault() { 
        return oldAnnotation.attributeFormDefault(); 
       } 

       @Override 
       public String location() { 
        return oldAnnotation.location(); 
       } 

       @Override 
       public Class<? extends Annotation> annotationType() { 
        return oldAnnotation.annotationType(); 
       } 
      }; 

      @SuppressWarnings("unchecked") 
      Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(pkgInfo); 
      annotations.put(XmlSchema.class, newAnnotation); 

      XmlSchema modifiedAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0]; 

Podczas kompilacji i wykonywania tego samego kodu z 1.8.0_05 otrzymuję komunikat o błędzie:

java.lang.NoSuchFieldException: annotations 
    at java.lang.Class.getDeclaredField(Class.java:2057) 

Wiem, że hack, przynajmniej wygląda jak jeden. Ale czy Java 8 działa tutaj zgodnie z oczekiwaniami? W jaki sposób muszę zmienić ten kod, który działa z Javą 8?

odpowiedzi Javassist są zbyt mile widziane;)

Odpowiedz

3

Java 8 zmieniło jak adnotacje są przechowywane wewnętrznie. Ponieważ używasz paskudnego hackowania refleksyjnego z zakodowaną nazwą pola, każda aktualizacja Java może potencjalnie ponownie uruchomić twój kod.

java.lang.Class:

/** 
* @since 1.5 
*/ 
public Annotation[] getAnnotations() { 
    return AnnotationParser.toArray(annotationData().annotations); 
} 

private volatile transient AnnotationData annotationData; 

private AnnotationData annotationData() { 
    while (true) { // retry loop 
     AnnotationData annotationData = this.annotationData; 
     int classRedefinedCount = this.classRedefinedCount; 
     if (annotationData != null && 
      annotationData.redefinedCount == classRedefinedCount) { 
      return annotationData; 
     } 
     // null or stale annotationData -> optimistically create new instance 
     AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount); 
     // try to install it 
     if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) { 
      // successfully installed new AnnotationData 
      return newAnnotationData; 
     } 
    } 
} 

private static class AnnotationData { 
    final Map<Class<? extends Annotation>, Annotation> annotations; 
    final Map<Class<? extends Annotation>, Annotation> declaredAnnotations; 

    // Value of classRedefinedCount when we created this AnnotationData instance 
    final int redefinedCount; 

    AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations, 
        Map<Class<? extends Annotation>, Annotation> declaredAnnotations, 
        int redefinedCount) { 
     this.annotations = annotations; 
     this.declaredAnnotations = declaredAnnotations; 
     this.redefinedCount = redefinedCount; 
    } 
} 

Przypuszczam, że można korzystać z nowych wartości pól, aby tymczasowo naprawić swój kod. Stałą poprawką jest zmiana samej adnotacji, zamiast dynamicznej modyfikacji jej w czasie wykonywania.

Field annotationDataField = Class.class.getDeclaredField("annotationData"); 
annotationDataField.setAccessible(true); 

Object annotationData = annotationDataField.get(pkgInfo); 

Field annotationsField = annotationData.getClass().getDeclaredField("annotations"); 
annotationsField.setAccessible(true); 

Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) annotationsField 
     .get(annotationData); 
annotations.put(XmlSchema.class, newAnnotation); 

XmlSchema modifiedAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0]; 
+0

Wiem, że każda aktualizacja java mogłaby złamać mój kod. Wszelkie sugestie na inny sposób? – Zarathustra

+0

@Zarathustra Dlaczego nie możesz zmienić adnotacji? – Jeffrey

+0

wygenerowany kod. Komunikuję się z usługą SOAP na różnych serwerach, wszyscy dzielą ten sam plik wsdl, oczekują przestrzeni docelowej, ponieważ zawiera ona nazwę hosta. – Zarathustra