Добавление аннотаций Java во время выполнения

Можно ли добавить аннотацию к объекту (в моем случае, в частности, к методу) во время выполнения?

Немного больше объяснений: у меня есть два модуля, moduleA и moduleB. модуль Б зависит от модуля А, который ничем не зависит. (modA – это мои основные типы данных и интерфейсы, и, например, modB – это db / data layer) modB также зависит от externalLibrary. В моем случае modB передает class от modA к externalLibrary, который требует определенных методов для annotations. Конкретные annotations – все это часть externalLib, и, как я уже сказал, modA не зависит от externalLib, и я хотел бы сохранить его таким образом.

Итак, возможно ли это, или у вас есть предложения по другим способам рассмотрения этой проблемы?

Невозможно добавить аннотацию во время выполнения, похоже, что вам нужно ввести адаптер, который использует модуль B, чтобы обернуть объект из модуля A, подвергая требуемые аннотированные методы.

Это возможно через библиотеку инструментов байт-кода, такую ​​как Javassist .

В частности, ознакомьтесь с classом AnnotationsAttribute для примера о том, как создавать / устанавливать annotations и раздел руководства по API байт-кода для общих рекомендаций по управлению файлами classов.

Это не просто и просто, но я бы не рекомендовал этот подход и предлагаю вам вместо этого ответить на вопрос Тома, если вам не нужно делать это для огромного количества classов (или указанные classы недоступны для вас до времени исполнения и, таким образом, записываются адаптер невозможен).

Также можно добавить аннотацию к Java-classу во время выполнения с использованием API отражения Java. По существу необходимо воссоздать внутренние карты аннотаций, определенные в classе java.lang.Class (или для Java 8, определенные во внутреннем classе java.lang.Class.AnnotationData ). Естественно, этот подход довольно хакен и может в любой момент сломаться для новых версий Java. Но для быстрого и грязного тестирования / прототипирования этот подход может быть полезен время от времени.

Пример концептуального примера для 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  void putAnnotation(Class c, Class annotationClass, Map valuesMap){ putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap)); } public static  void putAnnotation(Class c, Class 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  Object /*AnnotationData*/ createAnnotationData(Class c, Object /*AnnotationData*/ annotationData, Class annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Map, Annotation> annotations = (Map, Annotation>) AnnotationData_annotations.get(annotationData); Map, Annotation> declaredAnnotations= (Map, Annotation>) AnnotationData_declaredAnotations.get(annotationData); Map, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations); newDeclaredAnnotations.put(annotationClass, annotation); Map, 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 annotationForMap(final Class annotationClass, final Map valuesMap){ return (T)AccessController.doPrivileged(new PrivilegedAction(){ 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); } }); } } 

Пример использования:

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

Вывод:

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

Ограничения такого подхода:

  • Новые версии Java могут сломать код в любое время.
  • Вышеприведенный пример работает только для Java 8 – для работы с более старыми версиями Java потребуется проверка версии Java во время выполнения и соответственно изменение реализации.
  • Если аннотированный class переопределяется (например, во время отладки), аннотация будет потеряна.
  • Не прошли тщательную проверку; не уверен, есть ли какие-либо плохие побочные эффекты – используйте на свой страх и риск

Можно создавать annotations во время выполнения через прокси . Затем вы можете добавить их к вашим объектам Java через reflection, как это предлагается в других ответах (но вам, вероятно, было бы лучше найти альтернативный способ справиться с этим, поскольку беспорядок с существующими типами через reflection может быть опасным и трудно отлаживать).

Но это не очень просто … Я написал библиотеку, я надеюсь, что Javanna просто сделает это легко, используя чистый API.

Это в JCenter и Maven Central .

Используй это:

 @Retention( RetentionPolicy.RUNTIME ) @interface Simple { String value(); } Simple simple = Javanna.createAnnotation( Simple.class, new HashMap() {{ put( "value", "the-simple-one" ); }} ); 

Если какая-либо запись карты не соответствует объявленным полям annotations и типу (типам), генерируется Исключение. Если какое-либо значение, которое не имеет значения по умолчанию, отсутствует, генерируется Исключение.

Это позволяет предположить, что каждый экземпляр annotations, который был создан успешно, безопасен для использования в качестве экземпляра annotations компиляции.

В качестве бонуса эта библиотека также может анализировать classы аннотаций и возвращать значения annotations в виде карты:

 Map values = Javanna.getAnnotationValues( annotation ); 

Это удобно для создания мини-фреймворков.

  • Получение имени параметра метода
  • Добавление файлов в java classpath во время выполнения
  • Как загрузить файл jar во время выполнения
  • выполнить код c # во время выполнения из файла кода
  • Создать JPA EntityManager без файла конфигурации persistence.xml
  • Как установить среду java в Windows 8?
  • Инициировать объект с определенным временем выполнения
  • java runtime.getruntime () получение результата от выполнения командной строки
  • Динамическое изменение уровня журнала log4j
  • Как получить x и y windows программы на Java?
  • Компиляция и работа с JavaFX 2.1
  • Давайте будем гением компьютера.