AOP - 从截取的类访问受保护/私有属性

时间:2018-01-07 20:44:38

标签: java logging aop aspectj ajdt

我正在开发一个基本上是定期运行的很多进程的项目。每个进程都是一个不同的类,它扩展了我们创建的抽象类RunnableProcess,其中包含private属性Map<String, String> result和带有以下签名的抽象方法run

public abstract void run(Map processContext) throws IOException;

为了改善项目的模块化,我开始使用面向方面编程(AOP)拦截来自每个run的{​​{1}}个调用。我还在学习AOP,直到现在我还有以下代码:

RunnableProcess

它正在运行,但我想记录的信息不仅仅是import static org.slf4j.LoggerFactory.getLogger; import org.slf4j.Logger; import process.RunnableProcess; import java.util.Map; public aspect ProcessRunInterceptor { private Logger logger; pointcut runProcess() : call(void RunnableProcess.run(Map)); after(): runProcess() { logger = getLogger(thisJoinPoint.getClass()); logger.info("process run successfully"); } } 。我在拦截的类中,在上面提到的"process run successfully"属性中有这些信息。是否可以在建议中访问它而不改变result的实现?

我可以(不愿意,但如果它是唯一的选择......)将属性从RunnableProcess更改为private,但我不会将其更改为{{1} }。我也不想为它创建一个protected方法。

1 个答案:

答案 0 :(得分:1)

根据我对other question的回答,我会向您解释您可以做些什么。一些提示:

  • 您可以使用before()代替after()around()

  • 您不应在单例方面使用私有成员作为流程实例,因为如果您在多个线程中有异步进程,则该成员可能会被覆盖。因此,您的方法不是线程安全的,您应该使用局部变量。

  • 您不应该打印&#34;处理成功运行&#34;在after()建议中,因为after()也在异常后运行。所以你不能安全地假设这个过程成功运行,只有它完全运行。你应该写&#34;完成过程&#34;或类似的。顺便说一句,如果你想区分成功的进程和结束异常,你可能需要研究切入点类型after() returningafter() throwing()

  • 使用抽象基类而不直接定义成员result是没有意义的。如果您可以将其作为父项中的受保护成员,将其作为私有成员添加到每个子类中?我们还在这里做OOP(当然是在AOP旁边),对吗?优点是您可以使用基类直接从方面访问该成员,就像您在切入点中所做的那样。

以下是MCVE

流程类:

package de.scrum_master.app;

import java.io.IOException;
import java.util.Map;

public abstract class RunnableProcess {
  protected String result = "foo";

  public abstract void run(Map processContext) throws IOException;
}
package de.scrum_master.app;

import java.io.IOException;
import java.util.Map;

public class FirstRunnableProcess extends RunnableProcess {
  @Override
  public void run(Map processContext) throws IOException {
    System.out.println("I am #1");
    result = "first";
  }
}
package de.scrum_master.app;

import java.io.IOException;
import java.util.Map;

public class SecondRunnableProcess extends RunnableProcess {
  @Override
  public void run(Map processContext) throws IOException {
    System.out.println("I am #2");
    result = "second";
  }
}

驱动程序应用程序:

package de.scrum_master.app;

import java.io.IOException;

public class Application {
  public static void main(String[] args) throws IOException {
    new FirstRunnableProcess().run(null);
    new SecondRunnableProcess().run(null);
  }
}

<强>方面:

在这里,您只需将target()对象绑定到切入点中的参数,并在两个建议中使用它。

package de.scrum_master.aspect;

import static org.slf4j.LoggerFactory.getLogger;

import org.slf4j.Logger;
import de.scrum_master.app.RunnableProcess;
import java.util.Map;

public privileged aspect ProcessRunInterceptorProtocol {
  pointcut runProcess(RunnableProcess process) :
    call(void RunnableProcess.run(Map)) && target(process);

  before(RunnableProcess process): runProcess(process) {
    Logger logger = getLogger(process.getClass());
    logger.info("logger = " + logger);
    logger.info("running process = " + thisJoinPoint);
  }

  after(RunnableProcess process): runProcess(process) {
    Logger logger = getLogger(process.getClass());
    logger.info("finished process = " + thisJoinPoint);
    logger.info("result = " + process.result);
  }
}

使用JDK日志记录的控制台日志(删除了一些噪音):

INFORMATION: logger = org.slf4j.impl.JDK14LoggerAdapter(de.scrum_master.app.FirstRunnableProcess)
INFORMATION: running process = call(void de.scrum_master.app.FirstRunnableProcess.run(Map))
I am #1
INFORMATION: finished process = call(void de.scrum_master.app.FirstRunnableProcess.run(Map))
INFORMATION: result = first
INFORMATION: logger = org.slf4j.impl.JDK14LoggerAdapter(de.scrum_master.app.SecondRunnableProcess)
INFORMATION: running process = call(void de.scrum_master.app.SecondRunnableProcess.run(Map))
I am #2
INFORMATION: finished process = call(void de.scrum_master.app.SecondRunnableProcess.run(Map))
INFORMATION: result = second