2

AspectJを使用して、すべてのクラスにロガーフィールドを自動的に追加し、各クラスのボイラープレートロガー情報を個別にコーディングする必要がないようにしようとしています。これはかなり一般的な方法のように思えますが、これを実現する既存のアスペクトを見つけることができませんでした。

アスペクト自体はかなり単純です。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public privileged aspect Slf4jLoggerAspect{

    public interface Slf4jLogger{}
    private final Logger Slf4jLogger.logger = LoggerFactory.getLogger(getClass());

    declare parents: ????? extends Slf4jLogger;
}

私の大きな質問は、?????に何を入れるかです。TypePatternにすべてのクラスを表示させたいのですが、インターフェースは表示しません。*パターンを使用しようとすると、すべてのインターフェイスも取得されます。

AspectJに、クラスdefnのみをすべて選択できるものはありますか?

それとも、このようなものはすでに存在しますか?Rooでこれを見ると思っていた/期待していましたが、slf4jrooアドオンまたはアノテーションが見つかりません。

ありがとう!

エリック

4

3 に答える 3

4

まず、Javaでは、クラスは( "extends"キーワードを使用して)単一のスーパークラスしか持てませんが、複数のインターフェース(実装)を持つことができることを覚えておいてください。したがって、「extends」ではなく「implements」を使用するように変更することをお勧めします。

以下は、ラムニヴァス・ラダッドによるAspectJ in Action、第2版(第5章)で説明されているミックスインスタイルのアプローチを行う方法を示しています。このスタイルは、クラスにインターフェースを実装させることができ、デフォルトの実装を提供したい場合にうまく機能します。

package com.example.aop.attributes;

import java.util.Calendar;

import javax.persistence.Column;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;

/**
 * Aspect that adds a timestamp attribute to the entity along with the logic to
 * update that timestamp value just before it gets persisted to the database. It
 * also adds a creation timestamp to track when the object was originally
 * created.
 * 
 * @author tgh
 * 
 */
public interface Timestamped {

    public Calendar getCreationTimestamp();

    public void setCreationTimestamp(Calendar creationTimestamp);

    public Calendar getModificationTimestamp();

    public void setModificationTimestamp(Calendar modificationTimestamp);

    /**
     * AspectJ MixIn for any class which implements the interface. Provides a
     * default implementation using AspectJ. This is the style shown in
     * Manning's AspectJ in Action (2nd edition) for providing a default
     * implementation interface.
     * 
     * @author tgh
     */
    static aspect Impl {

        @NotNull
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "created", nullable=false)
        private Calendar Timestamped.creationTimestamp = Calendar.getInstance();

        @NotNull
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "last_modified", nullable=false)
        private Calendar Timestamped.modificationTimestamp = Calendar.getInstance();

        public Calendar Timestamped.getCreationTimestamp() {
            return this.creationTimestamp;
        }

        public void Timestamped.setCreationTimestamp(Calendar creationTimestamp) {
            this.creationTimestamp = creationTimestamp;
        }

        public Calendar Timestamped.getModificationTimestamp() {
            return this.modificationTimestamp;
        }

        public void Timestamped.setModificationTimestamp(Calendar modificationTimestamp) {
            this.modificationTimestamp = modificationTimestamp;
        }

        @PrePersist
        @PreUpdate
        private void Timestamped.updateModificationTimestampDuringPrePersistAndPreUpdate() {
            this.modificationTimestamp = Calendar.getInstance();
        }

    }
}

上記を使用するには、エンティティクラスに「implementsTimestamped」を追加するだけです。これは、メンバー(新しい属性)をクラスに紹介するミックスインを行う最も簡単な方法です。ソースの注釈を使用してミックスインを行うこともできます。

ソースを変更できない場合は、宣言された親をいじくり回す必要があります。クラス宣言にすでに「@Entity」が含まれているエンティティにのみタグを付ける場合、これは次のようになります。

declare parents: @Entity * implements Slf4jLoggerAspect;

または:

declare parents: (@MakeLoggable *) implements Slf4jLogger;

インターフェイスが別のパッケージに含まれている場合は、特定のパッケージのみにタグを付けるTypePatternを試すことができます(インターフェイスパッケージにはタグを付けません)。

declare parents: my.class.package.* implements Slf4jLogger;

また、AspectJ in Action(2nd Ed)の3.5章によると、TypePattern要素で二項演算子を使用することもできます。

declare parents: (@Entity *) || (@MakeLoggable *) implements Slf4jLogger;

上記が機能するかどうかは100%わかりませんが、「||」があります。二項演算子と私はそれが他の場所で使用されているのを見ました(しかしリンクが見つかりません)。

(いいえ、タイプパターンにクラスのみを表示し、インターフェイスは表示しないように指示できるとは思いません。hasMethod()とhasField()がある可能性がありますが、本では実験的なものとしてマークされています。)

于 2012-11-19T01:08:40.690 に答える
1

私はあなたがあなたが望んでいることを達成できるとは思わない。ITDでは、具体的なクラス名を指定する必要があります。例:

private Logger MyClass.logger;

MyClassは、ロガーフィールドを追加するクラスです。したがって、クラスごとにこれらの1つを追加する必要があります(春のルースタイル)。

このロガーフィールドを親クラスに追加して、すべてのクラスにこのクラスを拡張させることはできますが、これは、すでに他のクラスを拡張しているクラスでは機能しません。

このフィールドをインターフェイスに追加して、例のようにすべてのクラスにインターフェイスを実装させることができますが、インターフェイスのフィールドは静的なfinalであるため、getClass()を呼び出すことはできません。

別のロガー作成を使用できます。

Logger Slf4jLogger.logger = LoggerFactory.getLogger(Slf4jLogger.class);

ただし、これによりすべてのロギングイベントに同じカテゴリが使用されるため、ロギングが使用できなくなります。

根本的なアプローチは、Objectクラスに追加することです。

final Logger Object.logger = LoggerFactory.getLogger(getClass());

しかし、それは本当に物事を台無しにする可能性があります。これを試してみたところ、「影響を受けるタイプはウィーバーにさらされていません」と表示されます。

また、アスペクトを通じてロギングフィールドを追加することは良い習慣だとは思いません。従来の方法で各クラスの上部にロガーフィールドを追加しても問題はありません。AOPは慎重に使用する必要があります。

于 2012-11-28T00:00:05.910 に答える
0

ユーザーtgharoldがミックスインについて言及していたという理由だけで:AspectJ 1.6.4では、機能@DeclareMixinが導入されました-チケット#266552も参照してください。たぶんそれは助けになるでしょう、私は試していませんが、あなたのミックスインタイプの問題を解決することは有望なようです。

于 2013-07-10T13:37:11.267 に答える