ArchUnit 工具守护分层架构

简介

ArchUnit 利用反射和字节码技术获取所有的包、类、字段等信息,并通过特定的分析来确定对象之间的访问关系。ArchUnit 使用 ASM 作为字节码分析的工具,代价是 ArchUnit 的很多规则的定义不是类型安全的。

ArchUnit 支持的检查特性有:

  • 包依赖检查。
  • 类依赖检查。
  • 类和包的位置约定检查。
  • 继承检查。
  • 分层依赖检查。
  • 循环依赖检查(Spring 支持双向依赖往往会导致这个现象)。

ArchUnit 本身也是按照分层架构设计的,其 API 分为 三层:

  • Core:核心层,处理一些基本的类、字节码等操作,用于 import 一组类进行断言。
  • Lang:处理各种规则的语法和架构逻辑,以及一些基本的检查器。
  • Library:定义了一些更为复杂的预定义规则。

ArchUnit 开源地址

开源示例

使用示例

maven 中加入

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.tngtech.archunit</groupId>
    <artifactId>archunit-junit4</artifactId>
    <version>0.9.1</version>
    <scope>test</scope>
</dependency>

添加规则

@AnalyzeClasses 注解中填写自己项目包路径

import javax.persistence.Entity;

import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchUnitRunner;
import com.tngtech.archunit.lang.ArchRule;

import org.junit.runner.RunWith;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;

/**
 * @version 1.0
 * @since 1.0
 */
@RunWith(ArchUnitRunner.class) // Remove this line for JUnit 5!!
@AnalyzeClasses(packages = "com.demo.project") //要扫描的package
public class ArchunitTest {

    /** dao下的类应该以Dao结尾 */
    @ArchTest
    public static final ArchRule DAOs_must_reside_in_a_dao_package = classes()
            .that()
            .haveNameMatching(".*Dao")
            .should()
            .resideInAPackage("..dao..")
            .as("DAOs should reside in a package '..dao..'");

    /** controller下的类应该以Controller结尾 */
    @ArchTest
    public static final ArchRule CONTROLLERs_must_reside_in_a_dao_package = classes()
            .that()
            .haveNameMatching(".*Controller")
            .should()
            .resideInAPackage("..controller..")
            .as("DAOs should reside in a package '..dao..'");

    /** util下的类应该以Util结尾 */
    @ArchTest
    public static final ArchRule UTILs_must_reside_in_a_dao_package = classes()
            .that()
            .haveNameMatching(".*Util")
            .should()
            .resideInAPackage("..util..")
            .as("DAOs should reside in a package '..dao..'");

    /** 有Entity注解的类,应该在domain或者entity包下 */
    @ArchTest
    public static final ArchRule entities_must_reside_in_a_domain_package = classes()
            .that()
            .areAnnotatedWith(Entity.class)
            .should()
            .resideInAnyPackage("..domain..", "..entity..");

    /** 接口类,应该以I开头 */
    @ArchTest
    public static final ArchRule interface_className_must_start_with_I = classes()
            .that()
            .areInterfaces()
            .should()
            .haveSimpleNameStartingWith("I")
            .as("Interface should start with I");

    /** service下的类,只能被controller下或者时service下的类访问 */
    @ArchTest
    public static final ArchRule services_should_only_be_accessed_by_controllers_or_other_services = classes()
            .that()
            .resideInAPackage("..service..")
            .should()
            .onlyBeAccessed()
            .byAnyPackage("..controller..", "..service..");
    /** impl下的类,不能是interface */
    @ArchTest
    public static final ArchRule interfaces_must_not_be_placed_in_implementation_packages = noClasses()
            .that()
            .resideInAPackage("..impl..")
            .should()
            .beInterfaces();
    /** 接口类,类名不能以Interface结尾 */
    @ArchTest
    public static final ArchRule interfaces_should_not_have_names_ending_with_the_word_interface = noClasses()
            .that()
            .areInterfaces()
            .should()
            .haveNameMatching(".*Interface");
    /** service下的类,不能调用controller下的类 */
    @ArchTest
    public static final ArchRule services_should_not_access_controllers = noClasses()
            .that()
            .resideInAPackage("..service..")
            .should()
            .accessClassesThat()
            .resideInAPackage("..controller..");
    /** dao下的类,不能调用controller下的类 */
    @ArchTest
    public static final ArchRule controllers_should_not_access_dao = noClasses()
            .that()
            .resideInAPackage("..dao..")
            .should()
            .accessClassesThat()
            .resideInAnyPackage("..controller..");
    /** 不应该使用System.* */
    @ArchTest
    private final ArchRule NO_ACCESS_TO_STANDARD_STREAMS =  NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
    /** 不应该抛出Exception */
    @ArchTest
    private final ArchRule NO_GENERIC_EXCEPTIONS = NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
    /** 不应该使用java.util.logging来记录日志 */
    @ArchTest
    private final ArchRule NO_JAVA_UTIL_LOGGING = NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;
}

忽略某个规则

public class ArchitectureTest {

    // will run
    @ArchTest
    public static final ArchRule rule1 = classes().should()...

    // won't run
    @ArchIgnore
    @ArchTest
    public static final ArchRule rule2 = classes().should()...
}

忽略多个类,即不让Archunit扫描多个类

在resources下建个"archunit_ignore_patterns.txt"的文件(这个文件名称是固定的,不能修改),在将要忽略的类路径放入其中。

相关文章

转载请注明: 转载自 浮生一程
本文链接地址 ArchUnit 工具守护分层架构
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇