【问题标题】:Can AspectJ replace "new X" with "new SubclassOfX" in third-party library code?AspectJ 可以在第三方库代码中将“new X”替换为“new SubclassOfX”吗?
【发布时间】:2013-05-03 09:33:13
【问题描述】:

我正在研究 AspectJ,看看我们是否可以在我们的测试套件中使用它。

我们有一个相当大的第三方 Java 通信库硬连线以使用它自己的类(不实现任何接口),这反过来意味着我们需要一个物理后端并正确配置才能运行测试。

我正在研究我们的选项来取消此限制。一种可能性是创建麻烦类的子类,然后在加载第三方库时要求 AspectJ 简单地将“new X”替换为“new OurSubclassOfX”,但我是 AspectJ 的新手,并且从我对文档的简短浏览来看不是典型的用例。

AspectJ 可以做到这一点吗?配置 sn-p 会是什么?

【问题讨论】:

    标签: aop aspectj


    【解决方案1】:

    是的,这是可能的。让我们假设你有一个硬连线的类,可能从数据库中获取一些东西,并想通过一个方面来模拟它:

    package de.scrum_master.aop.app;
    
    public class HardWired {
        private int id;
        private String name;
    
        public HardWired(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public void doSomething() {
            System.out.println("Fetching values from database");
        }
    
        public int getSomething() {
            return 11;
        }
    
        @Override
        public String toString() {
            return "HardWired [id=" + id + ", name=" + name + "]";
        }
    }
    

    然后有一个小驱动应用程序使用那个类(不是接口):

    package de.scrum_master.aop.app;
    
    public class Application {
        public static void main(String[] args) {
            HardWired hw = new HardWired(999, "My object");
            System.out.println(hw);
            hw.doSomething();
            System.out.println(hw.getSomething());
        }
    }
    

    输出如下:

    HardWired [id=999, name=My object]
    Fetching values from database
    11
    

    现在你定义你的派生模拟类,它应该替换原来的用于测试目的:

    package de.scrum_master.aop.mock;
    
    import de.scrum_master.aop.app.HardWired;
    
    public class HardWiredMock extends HardWired {
        public HardWiredMock(int id, String name) {
            super(id, name);
        }
    
        @Override
        public void doSomething() {
            System.out.println("Mocking database values");
        }
    
        @Override
        public int getSomething() {
            return 22;
        }
    
        @Override
        public String toString() {
            return "Mocked: " + super.toString();
        }
    }
    

    最后你定义了一个切入点,并建议在每次构造函数调用期间替换原始值:

    package de.scrum_master.aop.aspect;
    
    import de.scrum_master.aop.app.HardWired;
    import de.scrum_master.aop.mock.HardWiredMock;
    
    
    public aspect MockInjector {
        HardWired around(int p1, String p2) : call(HardWired.new(int, String)) && args(p1, p2) {
            return new HardWiredMock(p1, p2);
        }
    }
    

    输出根据需要改变:

    Mocked: HardWired [id=999, name=My object]
    Mocking database values
    22
    

    每个类和构造函数都这样做一次,就可以了。为了概括该方法,您需要连接点属性,并且取决于您想要走多远,也许是反射,但这在这里非常简单。享受吧!

    【讨论】:

    • 这是一个非常详细的答案,这似乎正是我正在寻找的。谢谢!
    • 没那么快!我忘了提到,为了将它应用于第 3 方库,您需要通过 LTW 或通过编织二进制类并用替换 JAR 替换库来将其编织到库的代码中。前者更具动态性,后者更容易做到,如果没有安全管理器和签名阻止您替换原始 JAR。 ;-)
    • 我会看看什么最有效。在测试时,我可以完全控制托管 JVM 及其环境。
    • 如果您从事他们的工作,请确保将您的测试框架出售给第三方库的生产者。祝你好运!
    猜你喜欢
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    相关资源
    最近更新 更多