注:開發的編輯器: 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
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>
直接上代碼了,以下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就能夠了,參考這裏。工具
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()... }
通常出如今遺留系統中。this
圖2.1
好比咱們想在Archunit掃描時,忽略DemoInterface和AnnotationDemo,那麼在resources下建個"archunit_ignore_patterns.txt"的文件(這個文件名稱是固定的,不能修改),在將要忽略的類路徑放入其中,注意格式,以下圖2.2所示,參考其官網。
圖2.2
新建一個interface,名爲DemoInterface,以後運行List-2,結果以下圖1所示,因爲DemoInterface是interface,類名以Interface結尾(List-2中定義不能以Interface結尾),未以I開頭(List-2中定義要以I開頭):
圖3.1 運行List-2後報錯信息
能夠在Github上看Archunit的例子,Github地址: https://github.com/TNG/ArchUnit-Examples
實際項目中,代碼量較多,規範要作好,特別是人員流動是公司中很常見的。