0

假设我有以下包结构

some.package
    |-aaa.private
    |-aaa.public
    |-bbb.private
    |-bbb.public

我的架构会要求,我只从some.package.aaa..*to拨打电话some.package.bbb.public..*,反之亦然,只从some.package.bbb..*to拨打电话some.package.aaa.public..*。换句话说,如果我遍历“主要”包边界(例如从 aaa 到 bbb),我只想允许调用另一个主要包根目录中的公共包。

是否可以定义一个 AspectJ 切入点,它选择所有违反此规则的连接点?即如果我想写

declare error: inSomeMajorPackage() && callingNonPublicPackageOfOtherMajorPackage() :
    "Please make calls only to public interfaces of other major packages";

有没有办法定义这两个切入点,以便它们强制执行此规则?

4

2 回答 2

2

注意:由于代码示例,这将是一个冗长的答案。

我创建了一个示例项目,可以从Scrum-Master.de下载。包结构如下:

封装结构

如您所见,在主应用程序包de.scrum_master 下方有三个“主要”包commonfeature1feature2,每个包含子包pub(公共)和prv(私有)。此外,还有一个包含所有方面的aop包。每个pub/prv子包都包含一个虚拟类。

Java类如下:

package de.scrum_master.common.pub;

import de.scrum_master.common.prv.CommonPrivate;
import de.scrum_master.feature1.prv.Feature1Private;
import de.scrum_master.feature1.pub.Feature1Public;
import de.scrum_master.feature2.prv.Feature2Private;
import de.scrum_master.feature2.pub.Feature2Public;

public class Application {
    private int id;
    private String name;

    public Application(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public static void main(String[] args) {
        System.out.println(new Application    (1, "Application"));
        System.out.println(new CommonPrivate  (2, "Common (private)"));
        System.out.println(new Feature1Public (3, "Feature 1 (public)"));
        System.out.println(new Feature1Private(4, "Feature 1 (private)"));
        System.out.println(new Feature2Public (5, "Feature 2 (public)"));
        System.out.println(new Feature2Private(6, "Feature 2 (private)"));
    }

    @Override
    public String toString() {
        return "Application [id=" + id + ", name=" + name + "]";
    }
}
package de.scrum_master.common.prv;

public class CommonPrivate {
    private int id;
    private String name;

    public CommonPrivate(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "CommonPrivate [id=" + id + ", name=" + name + "]";
    }
}
package de.scrum_master.feature1.pub;

public class Feature1Public {
    private int id;
    private String name;

    public Feature1Public(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Feature1Public [id=" + id + ", name=" + name + "]";
    }
}
package de.scrum_master.feature1.prv;

import de.scrum_master.feature2.prv.Feature2Private;
import de.scrum_master.feature2.pub.Feature2Public;

public class Feature1Private {
    private int id;
    private String name;

    public Feature1Private(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        new Feature2Private(11111, "This should be illegal");
        new Feature2Public(22222, "This should be OK");
        return "Feature1Private [id=" + id + ", name=" + name + "]";
    }
}
package de.scrum_master.feature2.pub;

public class Feature2Public {
    private int id;
    private String name;

    public Feature2Public(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Feature2Public [id=" + id + ", name=" + name + "]";
    }
}
package de.scrum_master.feature2.prv;

import de.scrum_master.feature1.prv.Feature1Private;
import de.scrum_master.feature1.pub.Feature1Public;

public class Feature2Private {
    private int id;
    private String name;

    public Feature2Private(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        new Feature1Private(33333, "This should be illegal");
        new Feature1Public(44444, "This should be OK");
        return "Feature2Private [id=" + id + ", name=" + name + "]";
    }
}

现在我们需要我们的方面。更准确地说,我们需要一个抽象的基础方面和一个针对每个“主要”包的特定于包的具体子方面。这不是很好,但它有效。

抽象基础方面如下所示:

package de.scrum_master.aop;

public abstract aspect AccessController {
    // All method/constructor calls to base package
    pointcut basePackageCall():
        call(* de.scrum_master..*.*(..)) || call(de.scrum_master..*.new(..));

    // Method/constructor calls to public packages
    pointcut publicPackageCall() :
        call(* de.scrum_master..*.pub..*(..)) || call(de.scrum_master..*.pub..new(..));

    // Own "major" package. Please override in concrete sub-aspect like this:
    // within(de.scrum_master.mymajor..*) 
    pointcut ownPackage();

    // Method/constructor calls within own "major" package. Please override in concrete sub-aspect like this:
    // call(* de.scrum_master.mymajor..*(..)) || call(de.scrum_master.mymajor..new(..)) 
    pointcut ownPackageCall();

    pointcut forbiddenCall() :
        ownPackage() && basePackageCall() && !(publicPackageCall() || ownPackageCall());

    declare error: forbiddenCall() :
        "Illegal call to non-public foreign major package";
}

正如你所看到的,有两个切入点必须由如下子方面具体化:

package de.scrum_master.aop;

public aspect AccessController_Common extends AccessController {
    pointcut ownPackage() :
        within(de.scrum_master.common..*);

    pointcut ownPackageCall() :
        call(* de.scrum_master.common..*(..)) || call(de.scrum_master.common..new(..));
}
package de.scrum_master.aop;

public aspect AccessController_Feature1 extends AccessController {
    pointcut ownPackage() :
        within(de.scrum_master.feature1..*);

    pointcut ownPackageCall() :
        call(* de.scrum_master.feature1..*(..)) || call(de.scrum_master.feature1..new(..));
}
package de.scrum_master.aop;

public aspect AccessController_Feature2 extends AccessController {
    pointcut ownPackage() :
        within(de.scrum_master.feature2..*);

    pointcut ownPackageCall() :
        call(* de.scrum_master.feature2..*(..)) || call(de.scrum_master.feature2..new(..));
}

为新的“主要”包创建子方面就像复制和粘贴以及对相应包名称的小编辑一样简单。

如果你检查一下Application.main,你Feature1Private.toStringFeature2Private.toString发现我在那里构建了一些非法调用非公开的外国子包,总共四个。在 Eclipse 的问题视图中看起来像这样:

在此处输入图像描述

关于基本/子方面的更多话:虽然在建议中可以动态确定包名称并通过反射做一些更多的魔术,但declare error它基于可以在编译时静态确定的切入点。因此,我们必须在这里更加具体和明确,这要求我们为场景中的每个“主要”包都有一个子方面。替代方案将是一个包含每个单个包的切入点的大具体方面。我认为这看起来很难看。

现在享受我的解决方案,我认为它充分解决了您的问题。:-)

于 2013-08-11T10:32:15.133 回答
0

如果您想做的是强制或阻止某些包依赖项,您可以使用 JDepend 使用不同的方法。请参阅this blog post了解如何做,但这基本上意味着您需要编写一个测试,例如

@Test
public void verifyDependencies() {
    thisPackage()
        .dependsOn("my.super.fantastic.package")
    .andOn("my.sortof.ok.package")
    .andItShouldNotDependOn("that.pesky.vendor.package")
    .orOn("my.very.bad.package");

    assertItHasAllAndNoOtherDependencies();
}

正如您在对另一个答案的评论中提到的,这实际上并没有扩展,但您可以将数据放在 CSV、excel 或类似文件中,并使用数据来强制执行依赖关系。

如果您想商业化,请查看Structure 101,但我认为这不值得。

而且我忘了说,如果包之间存在循环依赖关系(aaa.public 和 bbb.public 相互依赖),这意味着你的设计存在缺陷。

于 2013-08-12T18:06:43.600 回答