First off, remember that in Java a class can only have a single super-class (using the "extends" keyword), but you can have multiple interfaces (implements). So you'll probably want to change to using "implements" isntead of "extends".
The following shows how to do a mix-in style approach as explained in AspectJ in Action, 2nd Edition by Ramnivas Laddad (chapter 5). This style works well when you are able to have classes implement an interface and you want to provide a default implementation.
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();
}
}
}
To use the above, you just add "implements Timestamped" to the entity class. That's the easiest way to do mix-ins where you introduce members (new attributes) to the class. You can also do mix-ins using annotations on the source.
In cases where you can't modify the source, you're going to have to play around with the declare parents. In the case of only tagging entities where they already had "@Entity" on their class declaration this would be:
declare parents: @Entity * implements Slf4jLoggerAspect;
Or:
declare parents: (@MakeLoggable *) implements Slf4jLogger;
If your interfaces are in a separate package, then you could try a TypePattern that only tags a specific package (and not the interfaces package):
declare parents: my.class.package.* implements Slf4jLogger;
And according to chapter 3.5 in AspectJ in Action (2nd Ed), you can also use binary operators on the Type Pattern element:
declare parents: (@Entity *) || (@MakeLoggable *) implements Slf4jLogger;
I'm not 100% sure that the above works, but there is an "||" binary operator and I've seen that used elsewhere (but can't find the link).
(And no, I don't think you can tell the Type Pattern to only look at classes and not interfaces. Although there is possibly hasMethod() and hasField() but those were marked as experimental in the book.)