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 中有什么东西可以让我只挑选出我所有的类定义吗?

或者这样的东西已经存在了吗?我本来想/期望在 Roo 中看到这一点,但找不到 slf4j roo 插件或注释。

谢谢!

埃里克

4

3 回答 3

4

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

下面展示了如何按照 Ramnivas Laddad 的 AspectJ in Action, 2nd Edition(第 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 (2nd Ed) 中的第 3.5 章,您还可以在 Type Pattern 元素上使用二元运算符:

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

我不能 100% 确定上述方法是否有效,但有一个“||” 二元运算符,我已经看到在其他地方使用过(但找不到链接)。

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

于 2012-11-19T01:08:40.690 回答
1

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

private Logger MyClass.logger;

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

您可以将此记录器字段添加到父类,并使所有类扩展该类,但这不适用于已经扩展其他类的类。

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

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

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

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

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

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

但这可能真的把事情搞砸了。刚试过这个,说“受影响的类型没有暴露给织布工”。

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

于 2012-11-28T00:00:05.910 回答
0

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

于 2013-07-10T13:37:11.267 回答