我正在尝试使用 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 指示所有类,但不指示接口(interface)。如果我尝试仅使用 * 模式,它也会拾取我的所有接口(interface)。
AspectJ 中有什么东西可以让我只挑选出我的所有类定义吗?
或者这样的东西已经存在了吗?我本想/期望在 Roo 中看到这一点,但找不到 slf4j roo 插件或注释。
谢谢!
埃里克
最佳答案
首先,请记住,在 Java 中,一个类只能有一个父类(super class)(使用“extends”关键字),但可以有多个接口(interface)(实现)。因此,您可能想要更改为使用“实现”而不是“扩展”。
下面展示了如何执行混合风格方法,如 Ramnivas Laddad 的《AspectJ in Action》第二版(第 5 章)中所述。当您能够让类实现接口(interface)并且您想要提供默认实现时,这种风格非常有效。
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();
}
}
}
要使用上面的内容,只需将“implements Timestamped”添加到实体类中即可。这是进行混合的最简单方法,您可以在其中向类引入成员(新属性)。您还可以使用源上的注释进行混合。
如果您无法修改源代码,您将不得不尝试声明父级。如果仅标记类声明中已包含“@Entity”的实体,则为:
declare parents: @Entity * implements Slf4jLoggerAspect;
或者:
declare parents: (@MakeLoggable *) implements Slf4jLogger;
如果您的接口(interface)位于单独的包中,那么您可以尝试仅标记特定包(而不是接口(interface)包)的 TypePattern:
declare parents: my.class.package.* implements Slf4jLogger;
根据 AspectJ in Action(第 2 版)中的第 3.5 章,您还可以在类型模式元素上使用二元运算符:
declare parents: (@Entity *) || (@MakeLoggable *) implements Slf4jLogger;
我不是100%确定上面的方法有效,但是有一个“||”二元运算符,我在其他地方看到过它的使用(但找不到链接)。
(不,我认为你不能告诉类型模式只查看类而不查看接口(interface)。虽然可能有 hasMethod() 和 hasField() 但它们在书中被标记为实验性的。)
关于java - 是否有适用于所有类(而非接口(interface))的 AspectJ TypePattern?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13422230/