简介
ArchUnit 利用反射和字节码技术获取所有的包、类、字段等信息,并通过特定的分析来确定对象之间的访问关系。ArchUnit 使用 ASM 作为字节码分析的工具,代价是 ArchUnit 的很多规则的定义不是类型安全的。
ArchUnit 支持的检查特性有:
- 包依赖检查。
- 类依赖检查。
- 类和包的位置约定检查。
- 继承检查。
- 分层依赖检查。
- 循环依赖检查(Spring 支持双向依赖往往会导致这个现象)。
ArchUnit 本身也是按照分层架构设计的,其 API 分为 三层:
- Core:核心层,处理一些基本的类、字节码等操作,用于 import 一组类进行断言。
- Lang:处理各种规则的语法和架构逻辑,以及一些基本的检查器。
- Library:定义了一些更为复杂的预定义规则。
使用示例
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"的文件(这个文件名称是固定的,不能修改),在将要忽略的类路径放入其中。