6

トグルJavaアノテーションを機能させるにはどうすればよいですか?

シンプルなフィーチャートグル:-if(toggle enabled)do x

Springでは、「プロファイル」を使用してBeanを切り替えることができます。

私はこれらを使用していて問題ありませんが、フィールドまたはクラスの注釈を切り替えたいのですが、どうすればよいですか?

ユースケースでは、jpaアノテーションを持つクラスがあります。特定の環境では、一部のフィールドが@transientであることを構成でマークできるようにしたいと思います。

4

6 に答える 6

4

前に述べたように、アノテーションを「無効化」しようとすることは、可能かもしれませんが、問題に取り組むための最良の方法ではありません。

Adrian Shum が言ったように、フレームワークが注釈を処理する方法を変更する必要があります。あなたの場合、JPA実装(Hibernateなど)の下にORMプロバイダーが必要です。

ほとんどの ORM には、カスタム機能を提供する何らかの方法があります。たとえば、Hibernate の場合、Interceptorを作成し、JPA 構成の persistence-unit に hibernate.ejb.interceptor を追加することで登録できます (詳しくはこちらを参照) 。

このインターセプターが何をすべきかはあなた次第ですが、別の注釈 (@ConditionalTransiet など) を使用することをお勧めします。 onLoad と onSave は、関連するフィールドをオブジェクトから消去します。

于 2012-11-21T08:06:55.513 に答える
3

可能なオプションの 1 つは、注釈を宣言する機能と、読み込み時のアスペクト ウィービングのスプリングの機能を備えた、aspectjを使用することです。

注釈は条件付きで宣言できないと思いますが、特定の環境に応じてクラスパスに入れることができる別の jar にいつでもコンパイルして、ロード時のウィーバーがそれを見つけることができるようにすることができます。


アップデート

ここには多くの有用な回答がありますが、アノテーションの無効化/有効化は、aspectj で遊んで非常に興味深いことがわかったので、サンプルを以下に示します。

Aspectj の最新バージョンはアノテーションの削除をサポートしていますが、今のところこの機能はフィールド アノテーションでのみ利用可能です。したがって、非常に便利な方法は、アノテーションをまったく宣言せず、アノテーションを有効にする必要がある場合に、jar をプリコンパイル済みのアスペクトに配置することです。前述のように、クラスパスへの注釈を有効にします。


サンプル


最初の壷

メインクラス

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>

クラスパスに 2 番目の jar を指定せずにアプリケーションを実行する

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

クラスパスの 2 番目の jar を使用してアプリケーションを実行する

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

したがって、アプリケーション ソースへの変更はまったく行われませんでした。

于 2012-11-21T05:05:49.377 に答える
1

いいえ、できません。

注釈は単なるメタデータです。ソースコードをコンパイルした後、バイトコードに添付されます(保持によって異なります)。したがって、それは常にそこにあります。通常の方法を使用して、実行時に非表示にすることはできません。

ただし、注釈は単なるメタデータです。それ自体は何もしません。他の誰かが注釈を検査し、それに応じて作業を行う必要があります。したがって、調査する必要があるのは、注釈を検査する「誰か」に、注釈を解釈する正しい方法を伝える方法を見つける必要があります(たとえば、注釈を無視するなど)。

このアクションを実行する一般的な方法はありません。すべて、アノテーションを検査している人に依存するためです。


あなたが厄介な方法を取ることを主張するならば、私はあなたが実行時にクラスを変えるかもしれないと信じています。退屈な作業になります。私が覚えているように、Javassistのようなツールを使用すると、クラスローダーによってロードされたクラスを「書き換え」て保存することができます。ただし、多くの問題に直面することになります。たとえば、クラスの変更プロセスは、他のコードが実行される前に実行する必要があります。そうでない場合、たとえば、Hibernateは変更されていないクラスをすでに検査してセットアップを行っており、削除した場合でもその後のクラスからのアノテーションは、何もしません。

于 2012-11-21T03:28:50.377 に答える
1

Hibernate の初期には、個別のマッピング xml ファイルを使用して、構成を実際のクラスから分離するように設計しました。注釈は、利便性への妥協として後で追加されただけです。

orm.xml 構成を使用してアノテーションをオーバーライドする、標準化された JPA でも可能です。リファレンスについては、 http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/xml-overriding.htmlを参照してください。

あなたの場合、を使用するmetadata-complete="true"と、すべてのメタデータは注釈からではなく orm.xml ファイルから取得されます。次に、2 つの異なる 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>

于 2012-11-26T23:39:50.177 に答える
1

フィールドレベルでは無理だと思います。あなたができることは、クラス全体をJPAによって考慮されないようにすることです(persistence-unit構成を介して)。これは、私が信じているプロファイルごとに実行できるはずです。

于 2012-08-22T03:44:35.703 に答える
1

これを試すことができます(アスペクトを使用):

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

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

ただし、プロファイル アノテーションがアスペクト クラスで機能するかどうかはわかりません。また、この方法では、この動作を適用するすべてのフィールドにこれらの側面を追加する必要があります。これよりも一般的なものにするのは難しいです。

于 2012-11-21T08:00:54.903 に答える