AOP - 从拦截的 class 访问 protected/private 属性
AOP - accessing a protected/private attribute from the intercepted class
我正在从事一个项目,该项目基本上包含很多 运行 周期性的流程。每个进程都是一个不同的class,它扩展了我们创建的一个抽象class RunnableProcess
,其中包含一个private
属性Map<String, String> result
和抽象方法run
签名如下:
public abstract void run(Map processContext) throws IOException;
为了改进项目的模块化,我开始使用面向方面编程 (AOP) 拦截来自每个 RunnableProcess
的 run
调用。我还在学习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
属性中截获的 class 中有此信息。是否可以在不更改 RunnableProcess
?
的实现的情况下在建议中访问它
我可以(不想,但如果这是唯一的选择...)将属性从 private
更改为 protected
,但我不会将其更改为 public
。我也不想为它创建一个 get
方法。
根据我对您 的回答,我将向您解释您可以做什么。一些提示:
而不是 before()
和 after()
你可以只使用 around()
.
您不应在单例方面为流程实例使用私有成员,因为如果您在多个线程中有异步流程,则该成员可能会被覆盖。因此,您的方法不是线程安全的,您应该改用局部变量。
您不应在 after()
通知中打印 "process run successfully",因为 after()
也会在异常后运行。所以你不能安全地假设进程 运行 成功,只是它 运行 而已。你应该写 "finished process" 或类似的。顺便说一句,如果你想区分成功的进程和以异常结束的进程,你可能需要查看切入点类型 after() returning
和 after() throwing()
.
使用抽象基 class 而不直接定义成员 result
是没有意义的。如果您可以将其作为父级中的受保护成员,为什么还要将其添加为每个子 class 中的私有成员?我们这里仍然在做 OOP(当然除了 AOP 之外),对吧?优点是您可以使用基 class 直接从切面访问成员,就像您在切入点中所做的那样。
这里有一个 MCVE 给你:
处理 classes:
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
我正在从事一个项目,该项目基本上包含很多 运行 周期性的流程。每个进程都是一个不同的class,它扩展了我们创建的一个抽象class RunnableProcess
,其中包含一个private
属性Map<String, String> result
和抽象方法run
签名如下:
public abstract void run(Map processContext) throws IOException;
为了改进项目的模块化,我开始使用面向方面编程 (AOP) 拦截来自每个 RunnableProcess
的 run
调用。我还在学习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
属性中截获的 class 中有此信息。是否可以在不更改 RunnableProcess
?
我可以(不想,但如果这是唯一的选择...)将属性从 private
更改为 protected
,但我不会将其更改为 public
。我也不想为它创建一个 get
方法。
根据我对您
而不是
before()
和after()
你可以只使用around()
.您不应在单例方面为流程实例使用私有成员,因为如果您在多个线程中有异步流程,则该成员可能会被覆盖。因此,您的方法不是线程安全的,您应该改用局部变量。
您不应在
after()
通知中打印 "process run successfully",因为after()
也会在异常后运行。所以你不能安全地假设进程 运行 成功,只是它 运行 而已。你应该写 "finished process" 或类似的。顺便说一句,如果你想区分成功的进程和以异常结束的进程,你可能需要查看切入点类型after() returning
和after() throwing()
.使用抽象基 class 而不直接定义成员
result
是没有意义的。如果您可以将其作为父级中的受保护成员,为什么还要将其添加为每个子 class 中的私有成员?我们这里仍然在做 OOP(当然除了 AOP 之外),对吧?优点是您可以使用基 class 直接从切面访问成员,就像您在切入点中所做的那样。
这里有一个 MCVE 给你:
处理 classes:
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