Функция переключения аннотаций Java

Как я могу переключать аннотации Java?

Простое переключение функций: - если (переключение включено) сделать x

Spring позволяет использовать «профили» для переключения компонентов.

Я использую их, и они в порядке, но я хотел бы переключить аннотацию на поле или классы... как я могу это сделать?

Вариант использования: у меня есть класс с аннотациями jpa. Я хочу иметь возможность пометить через конфигурацию, что некоторые поля являются @transient в определенных средах.


person user48545    schedule 22.08.2012    source источник
comment
Не обрабатывайте аннотации... метаданные хороши только в том случае, если вы их обрабатываете   -  person Aravind Yarram    schedule 22.08.2012
comment
Я не понимаю. Я хочу, чтобы аннотация существовала... но чтобы можно было отключить их с помощью переключения конфигурации/функций.   -  person user48545    schedule 22.08.2012


Ответы (6)


Один из возможных вариантов — использовать aspectj с его возможностью декларировать аннотации и возможности Spring переплетение аспектов времени загрузки.

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


ОБНОВЛЕНИЕ

Хотя здесь есть много полезных ответов, я нашел отключение/включение аннотаций довольно интересным, играя с аспектом, поэтому пример ниже.

Последние версии аспекта поддерживают удаление аннотаций, но пока эта возможность доступна только для аннотаций полей, поэтому весьма полезный способ — вообще не объявлять аннотации, а если их нужно включить — поставить банку с прекомпилированными аспектами, которые включит аннотации в путь к классам, как я упоминал ранее.


ОБРАЗЕЦ


Первая банка

Основной класс

package org.foo.bar;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
        MyClass myObj = context.getBean("myObj", MyClass.class);

        System.out.println(myObj);
        System.out.println(myObj.getValue1());
        System.out.println(myObj.getValue2());
    }

}

Класс, в котором мы будем объявлять аннотации

package org.foo.bar;

public class MyClass {

    @MyAnn("annotated-field-1")
    private String field1;
    private String field2;

    @MyAnn("annotated-method-1")
    public String getValue1() {
        String value = null;
        try {
            MyAnn ann = getClass().getDeclaredMethod("getValue1").getAnnotation(MyAnn.class);
            if(ann != null) {
                value = ann.value();
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return value;
    }

    public String getValue2() {
        String value = null;
        try {
            MyAnn ann = getClass().getDeclaredMethod("getValue2").getAnnotation(MyAnn.class);
            if(ann != null) {
                value = ann.value();
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return value;
    }

    @Override
    public String toString() {
        String field1 = null;
        try {
            MyAnn ann = getClass().getDeclaredField("field1").getAnnotation(MyAnn.class);
            if(ann != null) {
                field1 = ann.value();
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        String field2 = null;
        try {
            MyAnn ann = getClass().getDeclaredField("field2").getAnnotation(MyAnn.class);
            if(ann != null) {
                field2 = ann.value();
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        StringBuilder sb = new StringBuilder();
        sb.append("MyClass");
        sb.append("{field1='").append(field1).append('\'');
        sb.append(", field2='").append(field2).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

Сама аннотация

package org.foo.bar;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface MyAnn {

    String value();

}

Контекст приложения

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:load-time-weaver />

    <bean id="myObj" class="org.foo.bar.MyClass" />

</beans>

Вторая банка

Аспект

package org.foo.bar;

public aspect ToggleAnnotationAspect {

    declare @field : private String org.foo.bar.MyClass.field1 : -@MyAnn;
    declare @field : private String org.foo.bar.MyClass.field2 : @MyAnn("annotated-field-2");

    declare @method : public String org.foo.bar.MyClass.getValue2() : @MyAnn("annotated-method-2");

}

META-INF/aop.xml

<?xml version="1.0"?>

<aspectj>
    <aspects>
        <aspect name="org.foo.bar.ToggleAnnotationAspect"/>
    </aspects>
</aspectj>

Запуск приложения без второй банки в пути к классам

java -javaagent:spring-instrument-3.1.3.RELEASE.jar \
     -classpath app1.jar;<rest_of_cp> org.foo.bar.Main

будет печатать

MyClass{field1='annotated-field-1', field2='null'}
annotated-method-1
null

Запуск приложения со второй банкой в ​​пути к классам

java -javaagent:spring-instrument-3.1.3.RELEASE.jar \
     -classpath app1.jar;app1-aspects.jar;<rest_of_cp> org.foo.bar.Main

будет печатать

MyClass{field1='null', field2='annotated-field-2'}
annotated-method-1
annotated-method-2

Таким образом, никаких изменений в исходном коде приложения не производилось.

person szhem    schedule 21.11.2012

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

Как сказал Адриан Шум, вы должны изменить способ обработки аннотации фреймворком. В вашем случае должен быть какой-то поставщик ORM под вашей реализацией JPA (например, Hibernate).

У большинства ORM есть какой-либо способ предоставления пользовательских функций, например, в случае Hibernate вы можете создать Interceptor и зарегистрируйте его, добавив hibernate.ejb.interceptor в модуль сохранения в вашей конфигурации JPA, как описано здесь .

Что должен делать этот перехватчик, зависит от вас, но я бы предложил использовать другую аннотацию (например, @ConditionalTransiet), один из способов - просмотреть поля с помощью отражения, проверить, есть ли у них аннотация и если она находится в неправильной среде, а затем использовать onLoad и onSave стирают соответствующие поля из объекта.

person Alon Bar David    schedule 21.11.2012
comment
Действительно, это не имеет ничего общего с Spring. Вам нужно изменить способ обработки аннотаций ORM. Имхо использование Interceptor немного излишне. Использование простого файла orm.xml также должно помочь (см. мой ответ ниже). - person GreyFairer; 27.11.2012

Я не думаю, что это возможно на полевом уровне. Что вы могли бы сделать, так это исключить весь класс из рассмотрения JPA (через конфигурацию модуля сохранения). Я считаю, что это должно быть возможно сделать для каждого профиля.

person Peter Butkovic    schedule 22.08.2012

Нет, ты не можешь этого сделать.

Аннотация — это просто часть метаданных. Он прикрепляется к байт-коду (ну, в зависимости от удержания) после того, как вы скомпилировали исходный код. Поэтому оно всегда есть. Вы не можете заставить его исчезнуть во время выполнения обычным способом.

Однако аннотация — это просто метаданные. Само по себе ничего не делает. Должен быть кто-то еще, чтобы проверять аннотации и соответственно выполнять свою работу. Поэтому вы должны изучить, вы должны найти какой-то способ сказать этому «кому-то», кто проверяет аннотацию, и сказать ему, как правильно интерпретировать аннотацию (например, игнорировать некоторые аннотации и т. д.)

Общего способа выполнения этого действия нет, так как все зависит от того, кто просматривает аннотацию.


Если вы настаиваете на том, чтобы потрудиться, я думаю, вы можете изменить классы во время выполнения. Это будет утомительная работа. Насколько я помню, такие инструменты, как Javassist, позволяют вам «переписывать» классы, загруженные загрузчиком классов, и сохранять их обратно. Однако вы столкнетесь с множеством проблем, например, ваш процесс чередования классов должен происходить до запуска любого другого кода, если нет, например, Hibernate уже проверит немодифицированные классы и настроит их, и даже вы удалили аннотации из классов впоследствии, это ничего не сделает.

person Adrian Shum    schedule 21.11.2012

Вы можете попробовать это (используя аспекты):

@Profile("active")
privileged aspect AddField {
    private String MyClass.name;
}

@Profile("inactive")
privileged aspect AddFieldTransient {
    @Transient
    private String MyClass.name;
}

Однако не уверен, что аннотация профиля работает с классами аспектов. Кроме того, этот способ потребует от вас добавления этих аспектов для каждого отдельного поля, к которому вы хотите применить это поведение. Его трудно сделать более общим, чем это.

person Solubris    schedule 21.11.2012

В начале Hibernate они разработали его, чтобы отделить конфигурацию от реальных классов, используя отдельные XML-файлы сопоставления. Аннотации были добавлены позже в качестве компромисса для удобства.

Это все еще возможно в стандартизированном JPA, используя конфигурацию orm.xml для переопределения аннотаций. См. http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/xml-overriding.html для справки.

В вашем случае, если вы используете metadata-complete="true", все метаданные берутся из файла orm.xml, а не из аннотаций. Затем вы можете использовать два разных файла orm.xml.

<entity class="Administration" access="PROPERTY" metadata-complete="true">
    <attributes>
       <basic name="status"/>
       <basic name="optional">
     </attributes>

<entity class="Administration" access="PROPERTY" metadata-complete="true">
    <attributes>
       <basic name="status"/>
       <!-- omitted optional property -->
     </attributes>

person GreyFairer    schedule 26.11.2012