代碼質量之Archunit的使用

    注:開發的編輯器: Intellij Idea,JDK版本是JDK8html

    Archunit是什麼,官網的英文介紹很好,建議閱讀原文,"ArchUnit is a free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework. That is, ArchUnit can check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more"。java

    簡單來講,它是代碼格式、類之間的依賴關係檢查工具。git

  1. 進入官網
  2. 點擊右上角的"User Gruide"
  3. 以後就能夠看到它的英文教程文檔。

1.pom.xml中加入依賴

    List-1 最重要的是archunit-junit4依賴github

<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>

2.添加規則

    直接上代碼了,以下List-2所示,按項目狀況,修改本身的"@AnalyzeClasses的packages值"dom

    List-2編輯器

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;

/**
 * @author dmj1161859184@126.com 2018-09-14 23:03
 * @version 1.0
 * @since 1.0
 */
@RunWith(ArchUnitRunner.class) // Remove this line for JUnit 5!!
@AnalyzeClasses(packages = "com.mjduan.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;
}

    List-2中的代碼中已經給出註釋,本身能夠在項目中運行看下結果。不過發現有個很差的地方是,archunit會掃描test下的類。ide

    注:若是要忽略某個規則,那麼加上@ArchIgnore就能夠了,參考這裏工具

2.1 忽略某個規則

    List-3ui

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()...
}

2.2 忽略多個類,即不讓Archunit掃描多個類

    通常出如今遺留系統中。this

                     

                                                            圖2.1 

    好比咱們想在Archunit掃描時,忽略DemoInterface和AnnotationDemo,那麼在resources下建個"archunit_ignore_patterns.txt"的文件(這個文件名稱是固定的,不能修改),在將要忽略的類路徑放入其中,注意格式,以下圖2.2所示,參考其官網

    

                                                            圖2.2

3.Demo

    新建一個interface,名爲DemoInterface,以後運行List-2,結果以下圖1所示,因爲DemoInterface是interface,類名以Interface結尾(List-2中定義不能以Interface結尾),未以I開頭(List-2中定義要以I開頭):

                                                    圖3.1  運行List-2後報錯信息

4.官方的Archunit example

    能夠在Github上看Archunit的例子,Github地址: https://github.com/TNG/ArchUnit-Examples    

    實際項目中,代碼量較多,規範要作好,特別是人員流動是公司中很常見的。

相關文章
相關標籤/搜索