是否有适用于所有类(而不是接口)的AspectJ TypePattern



我正试图使用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中看到这一点,但找不到slf4j-Roo插件或注释。

谢谢!

Eric

首先,请记住,在Java中,一个类只能有一个超类(使用"extends"关键字),但可以有多个接口(实现)。因此,您可能希望更改为使用"implements"而不是"extends"。

以下展示了如何按照Ramnivas Laddad的《AspectJ in Action》第二版(第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();
}
}
}

要使用以上内容,只需将"implements Timestamped"添加到实体类中即可。这是将成员(新属性)引入类的最简单的混合方法。您还可以使用源上的注释进行混合。

在无法修改源的情况下,您将不得不处理声明的父级。在只标记类声明上已经有"@Entity"的实体的情况下,这将是:

declare parents: @Entity * implements Slf4jLoggerAspect;

或者:

declare parents: (@MakeLoggable *) implements Slf4jLogger;

如果你的接口在一个单独的包中,那么你可以尝试一个只标记特定包(而不是接口包)的TypePattern:

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

根据AspectJ in Action(第二版)中的第3.5章,您还可以在Type Pattern元素上使用二进制运算符:

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

我不能100%确定上面的方法是否有效,但有一个"||"二进制运算符,我在其他地方也见过(但找不到链接)。

(不,我不认为你可以告诉类型模式只看类而不看接口。虽然可能有hasMethod()和hasField(),但它们在书中被标记为实验性的。)

我不相信你能实现你想要的。对于ITD,您需要指定具体的类名,例如:

private Logger MyClass.logger;

其中MyClass是要添加记录器字段的类。因此,您必须为每个类添加一个(spring-roo样式)。

您可以将这个logger字段添加到父类中,并使所有类都扩展这个类,但这对已经扩展其他类的类不起作用。

您可以将这个字段添加到接口中,并使所有类实现您在示例中所拥有的接口,但这不会起作用,因为接口中的字段是静态的final,所以您不能调用getClass()。

您可以使用替代记录器创建:

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

但这将对所有日志事件使用相同的类别,从而使日志记录变得无用。

一个激进的方法可能是将其添加到Object类中:

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

但这真的会把事情搞砸。刚试过这个,并说"受影响的类型不会暴露于weaver"。

此外,我认为通过方面添加日志字段不是一个好的做法。我认为以传统方式在每个类的顶部添加Logger字段没有任何问题。AOP应该谨慎使用。

仅仅因为用户tgharold提到了mix-ins:在AspectJ 1.6.4中引入了@DeclareMixin功能-另请参阅ticket#266552。也许这有帮助,我还没有尝试过,但它似乎很有希望解决你的混合型问题。

最新更新