从 AspectJ 访问私有静态成员(例如记录器)

Accessing private static members (e.g. loggers) from AspectJ

假设我有这个class

public class MyClass {
    private Logger log = LogFactory.getLogger(MyClass.class);

    public void doSomething() {
        // doing something
    }
}

假设我要写一个切面来登录和退出:

public aspect TraceAspect {

    pointcut method(): execution(* *(..));

    before(): method(){

        log.info("entering method");
    }

    after(): method(){
        log.info("existing method");
    }
}

这里的问题是我想用aspect访问class里面的log对象,但是我不知道怎么办。我不想创建新的记录器,因为我想在记录时保留与 class 记录器关联的所有数据。有没有一种方法或模式可以访问 class 数据?

编辑:想声明这方面应该跟踪所有具有日志字段的 classes。也就是说,我可能有很多classes:MyClass, MyClass1, YourClass2, RepositoryClass,等等

Nándor 所说的在技术上是正确的,但我的建议是:请尽可能避免访问私有成员或方法,因为它们是私有的是有原因的。例如,即使 class 的 public 界面不变,它们也可能会发生变化。所以你永远不能依赖它们的存在或它们的命名。此外,横切关注点也应尽可能遵循封装等设计原则。

这个具体案例是关于 Slf4J 记录器的,更准确地说是关于您希望避免创建冗余记录器对象的。好吧,关于对象创建,Slf4J 并不像您想象的那样愚蠢或粗心。所有 classes 实现 ILoggerFactory 使用内部映射来缓存给定 (class) 名称的现有记录器,参见例如

那么,为什么不放松一下,使用目标 class 名称从您的方面访问相应的记录器呢?如果目标 class 没有自己的静态记录器,这甚至可以工作:

申请classes:

package de.scrum_master.app;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClassWithLogger {
  private Logger log = LoggerFactory.getLogger(MyClassWithLogger.class);

  public void doSomething() {}
}
package de.scrum_master.app;

public class MyClassWithoutLogger {
  public void doSomething() {}
}
package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    new MyClassWithLogger().doSomething();
    new MyClassWithoutLogger().doSomething();
  }
}

看点:

package de.scrum_master.aspect;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public aspect TraceAspect {
  pointcut executedMethods(Object targetObject) :
    execution(!static * *(..)) && target(targetObject);

  before(Object targetObject) : executedMethods(targetObject) {
    Logger log = LoggerFactory.getLogger(targetObject.getClass());
    log.info("Entering " + thisJoinPoint);
  }

  after(Object targetObject) : executedMethods(targetObject) {
    Logger log = LoggerFactory.getLogger(targetObject.getClass());
    log.info("Exiting " + thisJoinPoint);
  }
}

配置 Slf4J 以使用简单记录器的控制台日志:

[main] INFO de.scrum_master.app.MyClassWithLogger - Entering execution(void de.scrum_master.app.MyClassWithLogger.doSomething())
[main] INFO de.scrum_master.app.MyClassWithLogger - Exiting execution(void de.scrum_master.app.MyClassWithLogger.doSomething())
[main] INFO de.scrum_master.app.MyClassWithoutLogger - Entering execution(void de.scrum_master.app.MyClassWithoutLogger.doSomething())
[main] INFO de.scrum_master.app.MyClassWithoutLogger - Exiting execution(void de.scrum_master.app.MyClassWithoutLogger.doSomething())