我正在开发一个基本上是定期运行的很多进程的项目。每个进程都是一个不同的类,它扩展了我们创建的抽象类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
方法。
答案 0 :(得分:1)
根据我对other question的回答,我会向您解释您可以做些什么。一些提示:
您可以使用before()
代替after()
和around()
。
您不应在单例方面使用私有成员作为流程实例,因为如果您在多个线程中有异步进程,则该成员可能会被覆盖。因此,您的方法不是线程安全的,您应该使用局部变量。
您不应该打印&#34;处理成功运行&#34;在after()
建议中,因为after()
也在异常后运行。所以你不能安全地假设这个过程成功运行,只有它完全运行。你应该写&#34;完成过程&#34;或类似的。顺便说一句,如果你想区分成功的进程和结束异常,你可能需要研究切入点类型after() returning
和after() 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