2009-10-28 44 views
60

Có thể thêm chú thích vào đối tượng (trong trường hợp của tôi cụ thể là Phương pháp) khi chạy không?Thêm chú thích Java tại thời gian chạy

Để được giải thích thêm một chút: Tôi có hai mô đun, moduleA và moduleB. moduleB phụ thuộc vào moduleA, mà không phụ thuộc vào bất cứ điều gì. (ModA là các kiểu dữ liệu và các giao diện cốt lõi của tôi và như vậy, modB là lớp db/data) modB cũng phụ thuộc vào externalLibrary. Trong trường hợp của tôi, modB đang bàn giao một lớp từ modA sang externalLibrary, mà cần một số phương thức được chú thích. Các chú thích cụ thể là tất cả các phần của externalLib và, như tôi đã nói, modA không phụ thuộc vào externalLib và tôi muốn giữ nó theo cách đó.

Vì vậy, điều này có khả thi hay bạn có đề xuất về các cách khác để xem xét vấn đề này không?

+0

Kiểm tra này có thể được giúp bạn http://stackoverflow.com/a/14276270/4741746 ít nhất chúng ta có thể sửa đổi nó –

Trả lời

21

Không thể thêm chú thích khi chạy, có vẻ như bạn cần giới thiệu adapter mô-đun B sử dụng để bọc đối tượng từ mô-đun A hiển thị các phương thức được chú thích bắt buộc.

+1

Tôi thứ hai này. Nhưng tôi có thể xem xét để chú thích bản gốc, tôi không thấy một vấn đề lớn ở đây. Chúng ta thường làm như vậy, lấy trường hợp của các thực thể JPA, mà bạn chuyển tới một thành phần EJB từ xa để lưu trữ trong DB. Và bạn sử dụng tương tự để điền vào giao diện người dùng của mình. –

+0

Tom: Ah, tất nhiên rồi. Có lẽ với thừa kế: Mở rộng lớp từ mô-đun A, ghi đè lên phương thức được đề cập và sau đó chú thích nó? – Clayton

+0

Giấm: đó có lẽ là giải pháp dễ nhất cho tôi. Tôi đã cố gắng giữ cho "mô hình dữ liệu" riêng biệt với "triển khai dữ liệu" của mình, nhưng tôi thành thực không thấy thời gian khi tôi cần cắm vào một triển khai dữ liệu khác. – Clayton

38

Có thể thông qua thư viện công cụ bytecode như Javassist.

Cụ thể, hãy xem AnnotationsAttribute lớp để biết ví dụ về cách tạo/đặt chú thích và tutorial section on bytecode API để biết hướng dẫn chung về cách thao tác các tệp lớp học.

Tuy nhiên, tôi sẽ KHÔNG đề xuất phương pháp này và đề nghị bạn xem xét câu trả lời của Tom trừ khi bạn cần làm điều này cho một số lượng lớn các lớp học (hoặc các lớp học không có sẵn cho bạn cho đến khi thời gian chạy và do đó viết một adapter là không thể).

12

Cũng có thể thêm chú thích vào một lớp Java khi chạy bằng cách sử dụng API phản chiếu Java. Về cơ bản người ta phải tạo lại các bản đồ chú thích nội bộ được định nghĩa trong lớp java.lang.Class (hoặc đối với Java 8 được định nghĩa trong lớp nội bộ java.lang.Class.AnnotationData). Đương nhiên, cách tiếp cận này khá khó khăn và có thể phá vỡ bất cứ lúc nào cho các phiên bản Java mới hơn. Nhưng để thử nghiệm/tạo mẫu nhanh chóng và bẩn thì phương pháp này có thể hữu ích vào những thời điểm.

proove của khái niệm ví dụ cho Java 8:

public final class RuntimeAnnotations { 

    private static final Constructor<?> AnnotationInvocationHandler_constructor; 
    private static final Constructor<?> AnnotationData_constructor; 
    private static final Method Class_annotationData; 
    private static final Field Class_classRedefinedCount; 
    private static final Field AnnotationData_annotations; 
    private static final Field AnnotationData_declaredAnotations; 
    private static final Method Atomic_casAnnotationData; 
    private static final Class<?> Atomic_class; 

    static{ 
     // static initialization of necessary reflection Objects 
     try { 
      Class<?> AnnotationInvocationHandler_class = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 
      AnnotationInvocationHandler_constructor = AnnotationInvocationHandler_class.getDeclaredConstructor(new Class[]{Class.class, Map.class}); 
      AnnotationInvocationHandler_constructor.setAccessible(true); 

      Atomic_class = Class.forName("java.lang.Class$Atomic"); 
      Class<?> AnnotationData_class = Class.forName("java.lang.Class$AnnotationData"); 

      AnnotationData_constructor = AnnotationData_class.getDeclaredConstructor(new Class[]{Map.class, Map.class, int.class}); 
      AnnotationData_constructor.setAccessible(true); 
      Class_annotationData = Class.class.getDeclaredMethod("annotationData"); 
      Class_annotationData.setAccessible(true); 

      Class_classRedefinedCount= Class.class.getDeclaredField("classRedefinedCount"); 
      Class_classRedefinedCount.setAccessible(true); 

      AnnotationData_annotations = AnnotationData_class.getDeclaredField("annotations"); 
      AnnotationData_annotations.setAccessible(true); 
      AnnotationData_declaredAnotations = AnnotationData_class.getDeclaredField("declaredAnnotations"); 
      AnnotationData_declaredAnotations.setAccessible(true); 

      Atomic_casAnnotationData = Atomic_class.getDeclaredMethod("casAnnotationData", Class.class, AnnotationData_class, AnnotationData_class); 
      Atomic_casAnnotationData.setAccessible(true); 

     } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException e) { 
      throw new IllegalStateException(e); 
     } 
    } 

    public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, Map<String, Object> valuesMap){ 
     putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap)); 
    } 

    public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, T annotation){ 
     try { 
      while (true) { // retry loop 
       int classRedefinedCount = Class_classRedefinedCount.getInt(c); 
       Object /*AnnotationData*/ annotationData = Class_annotationData.invoke(c); 
       // null or stale annotationData -> optimistically create new instance 
       Object newAnnotationData = createAnnotationData(c, annotationData, annotationClass, annotation, classRedefinedCount); 
       // try to install it 
       if ((boolean) Atomic_casAnnotationData.invoke(Atomic_class, c, annotationData, newAnnotationData)) { 
        // successfully installed new AnnotationData 
        break; 
       } 
      } 
     } catch(IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e){ 
      throw new IllegalStateException(e); 
     } 

    } 

    @SuppressWarnings("unchecked") 
    private static <T extends Annotation> Object /*AnnotationData*/ createAnnotationData(Class<?> c, Object /*AnnotationData*/ annotationData, Class<T> annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
     Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) AnnotationData_annotations.get(annotationData); 
     Map<Class<? extends Annotation>, Annotation> declaredAnnotations= (Map<Class<? extends Annotation>, Annotation>) AnnotationData_declaredAnotations.get(annotationData); 

     Map<Class<? extends Annotation>, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations); 
     newDeclaredAnnotations.put(annotationClass, annotation); 
     Map<Class<? extends Annotation>, Annotation> newAnnotations ; 
     if (declaredAnnotations == annotations) { 
      newAnnotations = newDeclaredAnnotations; 
     } else{ 
      newAnnotations = new LinkedHashMap<>(annotations); 
      newAnnotations.put(annotationClass, annotation); 
     } 
     return AnnotationData_constructor.newInstance(newAnnotations, newDeclaredAnnotations, classRedefinedCount); 
    } 

    @SuppressWarnings("unchecked") 
    public static <T extends Annotation> T annotationForMap(final Class<T> annotationClass, final Map<String, Object> valuesMap){ 
     return (T)AccessController.doPrivileged(new PrivilegedAction<Annotation>(){ 
      public Annotation run(){ 
       InvocationHandler handler; 
       try { 
        handler = (InvocationHandler) AnnotationInvocationHandler_constructor.newInstance(annotationClass,new HashMap<>(valuesMap)); 
       } catch (InstantiationException | IllegalAccessException 
         | IllegalArgumentException | InvocationTargetException e) { 
        throw new IllegalStateException(e); 
       } 
       return (Annotation)Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, handler); 
      } 
     }); 
    } 
} 

Cách sử dụng Ví dụ:

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
public @interface TestAnnotation { 
    String value(); 
} 

public static class TestClass{} 

public static void main(String[] args) { 
    TestAnnotation annotation = TestClass.class.getAnnotation(TestAnnotation.class); 
    System.out.println("TestClass annotation before:" + annotation); 

    Map<String, Object> valuesMap = new HashMap<>(); 
    valuesMap.put("value", "some String"); 
    RuntimeAnnotations.putAnnotation(TestClass.class, TestAnnotation.class, valuesMap); 

    annotation = TestClass.class.getAnnotation(TestAnnotation.class); 
    System.out.println("TestClass annotation after:" + annotation); 
} 

Output:

TestClass annotation before:null 
TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some String) 

Hạn chế của phương pháp này:

  • Phiên bản Java mới có thể phá vỡ mã bất kỳ lúc nào.
  • Ví dụ trên chỉ hoạt động cho Java 8 - làm cho nó hoạt động cho các phiên bản Java cũ hơn sẽ yêu cầu kiểm tra phiên bản Java khi chạy và thay đổi việc triển khai tương ứng.
  • Nếu Lớp được chú thích nhận được redefined (ví dụ: trong khi gỡ lỗi), chú thích sẽ bị mất.
  • Không được kiểm tra kỹ lưỡng; không chắc chắn nếu có bất kỳ tác dụng phụ xấu - sử dụng có nguy cơ của riêng bạn ...
+0

Công việc tốt, tôi thực sự đánh giá cao để làm cho nó làm việc với Java 1.7, có lẽ bản đồ này là hữu ích: http://grepcode.com/file/repository .grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Class.java # Class.0annotations – gouessej

+1

Đây là tuyệt vời: D Thank cho công việc tuyệt vời –

+0

Bất kỳ đề xuất về cách có được điều này để làm việc cho lĩnh vực? – heez

1

Có thể tạo ra các chú thích trong thời gian chạy qua một Proxy.Sau đó, bạn có thể thêm chúng vào các đối tượng Java của bạn thông qua sự phản chiếu trong các câu trả lời khác (nhưng có lẽ bạn nên tìm cách thay thế để xử lý điều đó, như rối tung với các kiểu hiện có thông qua sự phản chiếu có thể nguy hiểm và khó gỡ lỗi).

Nhưng nó không phải là rất dễ dàng ... Tôi đã viết một thư viện được gọi là, tôi hy vọng một cách thích hợp, Javanna chỉ để làm điều này một cách dễ dàng bằng cách sử dụng một API sạch.

Đó là trong JCenterMaven Central.

Sử dụng nó:

@Retention(RetentionPolicy.RUNTIME) 
@interface Simple { 
    String value(); 
} 

Simple simple = Javanna.createAnnotation(Simple.class, 
    new HashMap<String, Object>() {{ 
     put("value", "the-simple-one"); 
    }}); 

Nếu bất kỳ mục của bản đồ không phù hợp với chú thích lĩnh vực khai báo (s) và loại (s), một ngoại lệ được ném. Nếu bất kỳ giá trị nào không có giá trị mặc định bị thiếu, ngoại lệ sẽ được ném.

Điều này làm cho nó có thể giả định mọi trường hợp chú thích được tạo thành công là như an toàn để sử dụng như là một trường hợp chú thích thời gian biên dịch.

Như một phần thưởng, lib này cũng có thể phân tích các lớp chú thích và trả lại giá trị của các chú thích như một bản đồ:

Map<String, Object> values = Javanna.getAnnotationValues(annotation); 

Đây là thuận tiện cho việc tạo mini-khung.

Các vấn đề liên quan