0

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

public abstract void run(Map processContext) throws IOException;

为了提高项目的模块化,我开始使用面向方面编程 (AOP) 来拦截run来自每个RunnableProcess. 我还在学习AOP,到目前为止我有以下代码:

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

我可以(不喜欢,但如果它是唯一的选择......)将属性从 更改privateprotected,但我不会将其更改为public. 我也不想get为它创建一个方法。

4

1 回答 1

1

在我对您其他问题的回答的基础上,我将向您解释您可以做什么。一些提示:

  • 而不是before()andafter()你可以只使用around().

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

  • after()您不应该在通知中打印“进程成功运行”,因为after()它也会在异常之后运行。因此,您不能安全地假设该过程成功运行,只能假设它完全运行。你应该写“完成的过程”或类似的。顺便说一句,如果您想区分成功的流程和以异常结束的此类流程,您可能需要查看切入点类型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
于 2018-01-08T01:37:20.130 回答