日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
全面掌握軟件架構(gòu)的守護(hù)神-ArchUnit

簡(jiǎn)要介紹

ArchUnit 是一個(gè)免費(fèi)、簡(jiǎn)單和可擴(kuò)展的庫(kù),可以使用任何普通的 Java 單元測(cè)試框架檢查 Java 代碼的架構(gòu)和編碼規(guī)則。

基本原理

ArchUnit 通過(guò)分析給定的 Java 字節(jié)碼,將所有類導(dǎo)入到 Java 代碼結(jié)構(gòu)中,來(lái)檢查包、類、層、切片上依賴關(guān)系,包括對(duì)循環(huán)依賴關(guān)系等問(wèn)題的檢查。

版本分支

ArchUnit 于2017年4月23日發(fā)布第一個(gè)版本,2022年10月3日發(fā)布了 1.0.0 版本,共32次Release。

ArchUnitNet 是一個(gè) 關(guān)于.NET/C# 的架構(gòu)測(cè)試工具。

體系結(jié)構(gòu)

  • 總覽

ArchUnit 由 ArchUnit、 ArchUnit-junit4、ArchUnit-junit5-api、 ArchUnit-junit5-engine 和
ArchUnit-junit5-engine-api 等模塊組成,還為最終用戶提供了 archunit-example 模塊。

  • ArchUnit

ArchUnit 模塊包含編寫(xiě)架構(gòu)測(cè)試所需的核心基礎(chǔ)結(jié)構(gòu),如ClassFileImporter、域?qū)ο蠛鸵?guī)則語(yǔ)法結(jié)構(gòu)。ArchUnit 分為 Core、Lang 和 Library 三層,Core 層處理基本的基礎(chǔ)結(jié)構(gòu),比如將字節(jié)碼導(dǎo)入為Java對(duì)象; Lang 層提供以簡(jiǎn)潔的方式制定架構(gòu)規(guī)則的語(yǔ)法; Library 層包含更為復(fù)雜的預(yù)定義規(guī)則,如多層分層架構(gòu)。

  • ArchUnit-Junit

ArchUnit-junit4 模塊包含與 JUnit 4集成的基礎(chǔ)結(jié)構(gòu),特別是用于緩存導(dǎo)入類的 ArchUnitRunner。

ArchUnit-junit5-* 模塊包含與 JUnit 5集成的基礎(chǔ)結(jié)構(gòu),并包含在測(cè)試運(yùn)行之間緩存導(dǎo)入類的基礎(chǔ)結(jié)構(gòu)。ArchUnit-junit5-API 包含用戶 API,用于編寫(xiě)支持 ArchUnit 的 JUnit 5的測(cè)試,ArchUnit-junit5-engine 包含運(yùn)行這些測(cè)試的運(yùn)行時(shí)引擎。
ArchUnit-junit5-engine-API 包含一些 API 代碼,這些 API 代碼用于那些想要對(duì)運(yùn)行 ArchUnit JUnit 5測(cè)試進(jìn)行更詳細(xì)控制的工具,特別是一個(gè) FieldSelector,它可以用來(lái)指示 ArchUnitTestEngine 運(yùn)行一個(gè)特定的規(guī)則字段(比較 JUnit 4和5 Support)。

  • ArchUnit-Example

archunit-example 模塊包含違反這些規(guī)則的示例體系結(jié)構(gòu)規(guī)則和示例代碼。在這里可以找到關(guān)于如何為項(xiàng)目設(shè)置規(guī)則的靈感,或者在 ArchUnit-最新發(fā)布版本的示例。

  • ArchUnit-Maven-Plugin

有一個(gè)maven插件arch-unit-maven-plugin,可以從 Maven 運(yùn)行 ArchUnit 規(guī)則。

安裝導(dǎo)入

要使用 ArchUnit,在類路徑中包含相應(yīng)的 JAR 文件就足夠了。

# junit4 maven 依賴,for junit4

com.tngtech.archunit
archunit-junit4
1.0.0
test


# junit5 maven 依賴,for junit5

com.tngtech.archunit
archunit-junit5
1.0.0
test

快速體驗(yàn)
@RunWith(ArchUnitRunner.class) // Junit5不需要這行
@AnalyzeClasses(packages = "com.mycompany.myapp") // ① 導(dǎo)入要分析的類
public class MyArchitectureTest {

@ArchTest // ② 方式一:使用靜態(tài)字段,對(duì)要分析的類的架構(gòu)規(guī)則進(jìn)行斷言
public static final ArchRule myRule = classes()
.that().resideInAPackage("..service..")
.should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");

@Test // ② 方式二:使用方法,并自行導(dǎo)入類,對(duì)要分析的類的架構(gòu)規(guī)則進(jìn)行斷言
public void Services_should_only_be_accessed_by_Controllers(){
JavaClasses importedClasses = new ClassFileImporter()
.importPackages("com.mycompany.myapp");

ArchRule myRule = classes()
.that().resideInAPackage("..service..")
.should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");

myRule.check(importedClasses);
}
}

詳細(xì)功能

  • 包依賴檢查
// 不允許任何 source 包中的類依賴于 foo 包中的類
noClasses().that().resideInAPackage("..source..")
.should().dependOnClassesThat().resideInAPackage("..foo..");

// foo 包中的類只能被 source.one 包和本包中的類依賴
classes().that().resideInAPackage("..foo..")
.should().onlyHaveDependentClassesThat()
.resideInAnyPackage("..source.one..", "..foo..")
  • 類依賴檢查
// 名為 *Bar 的類只能被名為 Bar 的類依賴 
classes().that().haveNameMatching(".*Bar")
.should().onlyHaveDependentClassesThat().haveSimpleName("Bar")

  • 類容器檢查
// Foo 開(kāi)頭的類只能放在 com.foo 包下
classes().that().haveSimpleNameStartingWith("Foo")
.should().resideInAPackage("com.foo")

  • 類繼承檢查
// 實(shí)現(xiàn) Connection 接口的類名稱只能以 Connection 結(jié)尾
classes().that().implement(Connection.class)
.should().haveSimpleNameEndingWith("Connection")

// 用到 EntityManager 的類只能在 persistence 包下
classes().that().areAssignableTo(EntityManager.class)
.should().onlyHaveDependentClassesThat()
.resideInAnyPackage("..persistence..")
  • 注解檢查
// 用到 EntityManager 的類需要依賴于 Transactional 注解
classes().that().areAssignableTo(EntityManager.class)
.should().onlyHaveDependentClassesThat()
.areAnnotatedWith(Transactional.class)
  • 分層檢查
layeredArchitecture()
.consideringAllDependencies()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..")

.whereLayer("Controller").mayNotBeAccessedByAnyLayer() // controller層不能被其它層訪問(wèn)
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") // service層只能被controller層訪問(wèn)
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service") // persistence層只能被service層訪問(wèn)

  • 循環(huán)依賴檢查
// com.myapp 的直屬子包間不能存在循環(huán)依賴
slices().matching("com.myapp.(*)..").should().beFreeOfCycles()

深入了解

  • 導(dǎo)入

//使用預(yù)定義導(dǎo)入選項(xiàng)從classpath導(dǎo)入類
new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS)
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importClasspath();

//從文件路徑導(dǎo)入類
JavaClasses classes = new ClassFileImporter().importPath("/some/path/to/classes");

// 自定義導(dǎo)入選項(xiàng),以忽略測(cè)試類
ImportOption ignoreTests = new ImportOption() {
@Override
public boolean includes(Location location){
return !location.contains("/test/"); // ignore any URI to sources that contains '/test/'
}
};
// 使用自定義規(guī)則從classpath導(dǎo)入類
JavaClasses classes = new ClassFileImporter()
.withImportOption(ignoreTests).importClasspath();
  • 概念模型

大多數(shù)對(duì)象類似于 Java 反射 API,包括繼承關(guān)系。因此,一個(gè) JavaClass 具有一些 JavaMember,JavaMember 可以是 JavaField、 JavaMethod、 JavaConstruction (或 JavaStaticInitializer)。

CodeUnit 雖然不存在于反射 API 中,但是為任何可以訪問(wèn)其他代碼的東西引入一個(gè)概念是有意義的。它要么是一個(gè)方法,一個(gè)構(gòu)造函數(shù)(包括類初始化器) ,要么是一個(gè)類的靜態(tài)初始化器(例如靜態(tài)塊,靜態(tài)字段賦值,等等)。

對(duì)另一個(gè)類的訪問(wèn)也是一個(gè)不在反射范疇的概念,ArchUnit在最細(xì)粒度上,只能從 CodeUnit 通過(guò) JavaFieldAccess 、JavaMethodCall、JavaConstructorCall 來(lái)分別訪問(wèn)字段、方法或構(gòu)造函數(shù)。

由于被訪問(wèn)的字段、方法、構(gòu)造函數(shù)可能定義在超類中,所以引入了FieldAccessTarget、MethodCallTarget、ConstructorCallTarget等Target系列概念,用于解析到真正的目標(biāo)類。

由于導(dǎo)入的類集并不總是包含所有的類,所以上圖中resolves to可能解析到0個(gè)對(duì)象。

另外,上圖中MethodCallTarget可以resolves to多個(gè)JavaMethod,其原因在于某個(gè)方法可能實(shí)現(xiàn)了多個(gè)接口,如下圖所示。

  • Core API 和 Lang API

Core API具備強(qiáng)大的功能,但是Lang API更為簡(jiǎn)潔。

// 本段代碼為使用Core API斷言規(guī)則
Set services = new HashSet<>();
for (JavaClass clazz : classes) {
// choose those classes with FQN with infix '.service.'
if (clazz.getName().contains(".service.")) {
services.add(clazz);
}
}

for (JavaClass service : services) {
for (JavaAccess access : service.getAccessesFromSelf()) {
String targetName = access.getTargetOwner().getName();

// fail if the target FQN has the infix ".controller."
if (targetName.contains(".controller.")) {
String message = String.format(
"Service %s accesses Controller %s in line %d",
service.getName(), targetName, access.getLineNumber());
Assert.fail(message);
}
}
}

// 如下代碼片段為使用Lang API實(shí)現(xiàn)如上相同的規(guī)則斷言
ArchRule rule = ArchRuleDefinition.noClasses()
.that().resideInAPackage("..service..")
.should().accessClassesThat().resideInAPackage("..controller..");

rule.check(importedClasses);

// 如下代碼展示Lang API提供的 and、or 等組合功能
noClasses()
.that().resideInAPackage("..service..")
.or().resideInAPackage("..persistence..")
.should().accessClassesThat().resideInAPackage("..controller..")
.orShould().accessClassesThat().resideInAPackage("..ui..")

rule.check(importedClasses);

Lang 層除了為類提供API之外,還為其成員提供了正反兩個(gè)系列的API,包括members()、noMembers()、fields()、noFields()、codeUnits()、noCodeUnits()、constructors()、noConstructors()等。

// 如下代碼片段展示與成員方法有關(guān)的API
ArchRule rule = ArchRuleDefinition.methods()
.that().arePublic()
.and().areDeclaredInClassesThat().resideInAPackage("..controller..")
.should().beAnnotatedWith(Secured.class);

rule.check(importedClasses);

  • 自定義規(guī)則

在ArchUnit,大多數(shù)規(guī)則都是如下架構(gòu)。

classes that ${PREDICATE} should ${CONDITION}

如果預(yù)定義API不能滿足要求,可以自定義規(guī)則。

// 定義一個(gè) Predicate
DescribedPredicate haveAFieldAnnotatedWithPayload =
new DescribedPredicate("have a field annotated with @Payload"){
@Override
public boolean apply(JavaClass input){
boolean someFieldAnnotatedWithPayload = // iterate fields and check for @Payload
return someFieldAnnotatedWithPayload;
}
};

// 定義一個(gè)Condition
ArchCondition onlyBeAccessedBySecuredMethods =
new ArchCondition("only be accessed by @Secured methods") {
@Override
public void check(JavaClass item, ConditionEvents events){
for (JavaMethodCall call : item.getMethodCallsToSelf()) {
if (!call.getOrigin().isAnnotatedWith(Secured.class)) {
String message = String.format(
"Method %s is not @Secured", call.getOrigin().getFullName());
events.add(SimpleConditionEvent.violated(call, message));
}
}
}
};

// 對(duì)類集應(yīng)用 Predicate 和 Condition
classes().that(haveAFieldAnnotatedWithPayload).should(onlyBeAccessedBySecuredMethods);

  • 控制規(guī)則文案
// 對(duì)于不常見(jiàn)的規(guī)則,最好按照如下方法記錄其理由
classes().that(haveAFieldAnnotatedWithPayload)
.should(onlyBeAccessedBySecuredMethods)
.because("@Secured methods will be intercepted, checking for increased privileges " +
"and obfuscating sensitive auditing information");

// 如果規(guī)則復(fù)雜,且自動(dòng)生成的規(guī)則文本太復(fù)雜,可以使用如下方式完全覆蓋規(guī)則說(shuō)明
classes().that(haveAFieldAnnotatedWithPayload)
.should(onlyBeAccessedBySecuredMethods)
.as("Payload may only be accessed in a secure way");

  • 忽略違規(guī)情況

因遺留代碼或其它無(wú)法滿足規(guī)則的情況,可以將一個(gè)名為
archunit_ignore_patterns.txt 的文本文件放在classpath的根目錄下,并在每一行使用一個(gè)可以匹配要忽略的沖突的正則表達(dá)式。

# 這里可以寫(xiě)上忽略的原因
.*some\.pkg\.LegacyService.*

  • 架構(gòu)檢查

ArchUnit 在 Library 層預(yù)定義了若干架構(gòu)檢查的 API。目前可以方便地檢查分層架構(gòu)和洋蔥架構(gòu),將來(lái)可能會(huì)擴(kuò)展到管道、過(guò)濾器,以及業(yè)務(wù)和技術(shù)關(guān)注點(diǎn)分離等。

// 架構(gòu)檢查的入口點(diǎn)
com.tngtech.archunit.library.Architectures

// 以下是對(duì)分層架構(gòu)的檢查示例
layeredArchitecture()
.consideringAllDependencies()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..")

.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")

// 以下為對(duì)洋蔥架構(gòu)(又稱六邊形架構(gòu)、端口和適配器架構(gòu))的檢查示例
onionArchitecture()
.domainModels("com.myapp.domain.model..")
.domainServices("com.myapp.domain.service..")
.applicationServices("com.myapp.application..")
.adapter("cli", "com.myapp.adapter.cli..")
.adapter("persistence", "com.myapp.adapter.persistence..")
.adapter("rest", "com.myapp.adapter.rest..");

  • 切片檢查
// 切片檢查的入口點(diǎn)
com.tngtech.archunit.library.dependencies.SlicesRuleDefinition

// 檢查 myapp 包的下一級(jí)子包中的類不存在循環(huán)依賴
SlicesRuleDefinition.slices()
.matching("..myapp.(*)..")
.should().beFreeOfCycles()

// 檢查 myapp 包的所有子包中的類不存在循環(huán)依賴
SlicesRuleDefinition.slices()
.matching("..myapp.(**)")
.should().notDependOnEachOther()

// 檢查 myapp 包和 service 包之間的包中的類不存在相互依賴情況
SlicesRuleDefinition.slices()
.matching("..myapp.(**).service..")
.should().notDependOnEachOther()

如果以上切片不能滿足要求,還可以使用SliceAssignment類來(lái)定制切片。

  • 編碼檢查

ArchUnit 通過(guò) GeneralCodingRules 類提供了一組通用性較高的編碼規(guī)則檢查。

  • 依賴檢查

DependencyRules 類提供了一組檢查類之間依賴關(guān)系的規(guī)則和條件。

  • 代理檢查

ProxyRules 提供了關(guān)于使用代理對(duì)象的檢查。

  • 基于PlantUML檢查

ArchUnit 在
com.tngtech.archunit.library.plantuml 包下提供了一個(gè)支持 PlantUML 的特性,用于直接從 PlantUML 派生出檢查規(guī)則,對(duì)相應(yīng)的類進(jìn)行檢查。

URL myDiagram = getClass().getResource("my-diagram.puml");

classes().should(
adhereToPlantUmlDiagram(myDiagram,
consideringAllDependencies())
);

支持的UML只能是組件圖,用Java類的Package作為組件原型。ArchUnit對(duì)組件圖還有一些特殊要求,同時(shí)提供一些檢查的額外選項(xiàng)。

' 如果使用如下組件圖進(jìn)行檢查,target中的類依賴source中的類將違反規(guī)則
@startuml
[某個(gè)源組件] <<..some.source..>>
[某個(gè)目標(biāo)組件] <<..some.target..>> as target

[某個(gè)源組件] --> target
@enduml

  • 凍結(jié)

當(dāng)違規(guī)行為過(guò)多,無(wú)法立即修復(fù)時(shí),需要建立一種迭代機(jī)制,防止代碼基線進(jìn)一步惡化。

ArchUnit 的 FreezingArchRule 類提供這方面的幫助,將現(xiàn)有違規(guī)行為記錄到 ViationStore 中,然后后續(xù)檢查只報(bào)告新增的違規(guī)行為,并忽略已知的違規(guī)。一旦違規(guī)得到修復(fù),F(xiàn)reezingArchRule 將自動(dòng)將其從已知沖突中去除,無(wú)需額外回歸。代碼行號(hào)的變化不會(huì)影響違規(guī)行為。

// 凍結(jié)某個(gè)規(guī)則
ArchRule rule = FreezingArchRule
.freeze(classes().should()./*complete ArchRule*/);

FreezingArchRule 默認(rèn)使用一個(gè)簡(jiǎn)單的純文本文件保存 ViationStore,以便利用 VCS 進(jìn)行跟蹤管理。該文件的路徑包括 ViationStore 的創(chuàng)建和更新行為也是可配置的,方便用于CI環(huán)境。

# ViationStore 文件
freeze.store.default.path=/some/path/in/a/vcs/repo
# 是否允許創(chuàng)建 ViationStore,默認(rèn)為 false
freeze.store.default.allowStoreCreatinotallow=true
# 是否允許更新 ViationStore,默認(rèn)為 true
freeze.store.default.allowStoreUpdate=false

# 是否允許重新凍結(jié)所有違規(guī)行為,表示隨時(shí)接受新的違規(guī)而只報(bào)告成功,默認(rèn)為 false
freeze.refreeze=true

# 支持自定義凍結(jié)存儲(chǔ)(繼承com.tngtech.archunit.library.freeze.ViolationStore)
freeze.store=fully.qualified.name.of.MyCustomViolationStore
# 如下行用于為自定義存儲(chǔ)類設(shè)置屬性
freeze.store.propOne=valueOne
freeze.store.propTwo=valueTwo

# 支持自定義違規(guī)行匹配器
freeze.lineMatcher=fully.qualified.name.of.MyCustomLineMatcher
  • 度量

與代碼質(zhì)量度量(如圈復(fù)雜度或方法長(zhǎng)度)類似,軟件架構(gòu)度量力求度量軟件的結(jié)構(gòu)和設(shè)計(jì)。

ArchUnit 可以用來(lái)計(jì)算一些眾所周知的軟件體系結(jié)構(gòu)度量。

import com.tngtech.archunit.library.metrics.ArchitectureMetrics;
// ...

JavaClasses classes = // ...
Set packages = classes.getPackage("com.example").getSubpackages();

// These components can also be created in a package agnostic way, compare MetricsComponents.from(..)
MetricsComponents components = MetricsComponents.fromPackages(packages);

// 計(jì)算 John Lakos 提出的依賴度量指標(biāo),指示系統(tǒng)組件間依賴程度
LakosMetrics metrics = ArchitectureMetrics.lakosMetrics(components);
// CCD 累積組件依賴,加總所有組件所有向外依賴數(shù)
System.out.println("CCD: " + metrics.getCumulativeComponentDependency());
// ACD 平均組件依賴,CCD除以組件數(shù)
System.out.println("ACD: " + metrics.getAverageComponentDependency());
// RACD 相對(duì)平均依賴,ACD除以組件數(shù)
System.out.println("RACD: " + metrics.getRelativeAverageComponentDependency());
// NCCD 系統(tǒng)的 CCD 除以具有相同數(shù)量成分的平衡二叉搜索樹(shù)的 CCD
System.out.println("NCCD: " + metrics.getNormalizedCumulativeComponentDependency());

// 計(jì)算 Robert C. Martin 提出的度量指標(biāo),指示組件之間的耦合度
ComponentDependencyMetrics metrics = ArchitectureMetrics.componentDependencyMetrics(components);
//CE 傳出耦合,對(duì)任何其它組件的依賴數(shù)
System.out.println("Ce: " + metrics.getEfferentCoupling("com.cdxwcx.component"));
//CA 傳入耦合,來(lái)自任何其它組件的依賴數(shù)
System.out.println("Ca: " + metrics.getAfferentCoupling("com.cdxwcx.component"));
// I 不穩(wěn)定性,Ce/(Ca + Ce)
System.out.println("I: " + metrics.getInstability("com.cdxwcx.component"));
// A 抽象性,組件內(nèi)抽象類的數(shù)量 / 組件中所有類的數(shù)量
// 在 ArchUnit 中,抽象值僅基于公共類,即從外部可見(jiàn)的類。
System.out.println("A: " + metrics.getAbstractness("com.cdxwcx.component"));
// D 距離主序列, | A + I - 1 |, 即距離(A = 1,I = 0)和(A = 0,I = 1)之間的理想線的歸一化距離
System.out.println("D: " + metrics.getNormalizedDistanceFromMainSequence("com.cdxwcx.component"));

// 計(jì)算 Herbert Dowalil 提出的可見(jiàn)性指標(biāo),指示組件的信息隱藏能力
VisibilityMetrics metrics = ArchitectureMetrics.visibilityMetrics(components);
// RV 相對(duì)可見(jiàn)性,當(dāng)前組件中可見(jiàn)元素?cái)?shù)量 / 當(dāng)前組件中所有元素?cái)?shù)量
System.out.println("RV : " + metrics.getRelativeVisibility("com.cdxwcx.component"));
// ARV 平均相對(duì)能見(jiàn)度,RV的均值
System.out.println("ARV: " + metrics.getAverageRelativeVisibility());
// GRV 全局相對(duì)可見(jiàn)性,所有組件中的可見(jiàn)元素?cái)?shù)量 / 所有組件中素有元素?cái)?shù)量
System.out.println("GRV: " + metrics.getGlobalRelativeVisibility());

  • JUnit支持
// 以下為基本用法,項(xiàng)目太大時(shí),會(huì)因?yàn)轭惖膶?dǎo)入導(dǎo)致性能較差,也容易出錯(cuò)
@Test
public void rule1(){
JavaClasses importedClasses = new ClassFileImporter().importClasspath();

ArchRule rule = classes()...

rule.check(importedClasses);
}

// 以下為正常用法
// 緩存基于測(cè)試類,同一個(gè)測(cè)試類中聲明的多個(gè)規(guī)則重用緩存
// 緩存基于導(dǎo)入位置,從相同URI導(dǎo)入時(shí)會(huì)發(fā)生重用,這種形式為軟引用,內(nèi)存不足時(shí)會(huì)被清除
@RunWith(ArchUnitRunner.class) // 此行JUnit5不需要
@AnalyzeClasses(packages = "com.myapp")
public class ArchitectureTest {

// 可將規(guī)則聲明為靜態(tài)字段
@ArchTest
public static final ArchRule rule1 = classes().should()...

@ArchTest
public static final ArchRule rule2 = classes().should()...

@ArchTest
public static void rule3(JavaClasses classes){
// 靜態(tài)方法,會(huì)使用緩存
}

}

  • 控制導(dǎo)入范圍
// 控制要導(dǎo)入的類
@AnalyzeClasses(packages = {"com.myapp.subone", "com.myapp.subtwo"})

// 也可以利用具有代表性的類,會(huì)導(dǎo)入該類所在包的所有類,這種方式便于重構(gòu)
@AnalyzeClasses(packagesOf = {SubOneConfiguration.class, SubTwoConfiguration.class})

// 也可以通過(guò)實(shí)現(xiàn) LocationProvider 來(lái)控制要導(dǎo)入哪些類
public class MyLocationProvider implements LocationProvider {
@Override
public Set get(Class testClass) {
// Determine Locations (= 分享題目:全面掌握軟件架構(gòu)的守護(hù)神-ArchUnit
分享URL:http://www.5511xx.com/article/dhohjge.html