まず、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()がある可能性がありますが、本では実験的なものとしてマークされています。)