【问题标题】:AOP - accessing a protected/private attribute from the intercepted classAOP - 从拦截的类访问受保护/私有属性
【发布时间】:2018-01-07 20:44:37
【问题描述】:

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

public abstract void run(Map processContext) throws IOException;

为了改进项目的模块化,我开始使用面向方面编程 (AOP) 来拦截来自每个 RunnableProcessrun 调用。我还在学习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的实现的情况下在advice中访问它?

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

【问题讨论】:

    标签: java logging aop aspectj ajdt


    【解决方案1】:

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

    • 您可以只使用around(),而不是before()after()

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

    • 您不应在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
    

    【讨论】:

      猜你喜欢
      • 2016-08-13
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 2023-03-04
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      相关资源
      最近更新 更多