? ? 注:开发的编辑器: Intellij Idea,JDK版本是JDK8
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"。
? ? 简单来说,它是代码格式、类之间的依赖关系检查工具。
List-1?最重要的是archunit-junit4依赖
<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值"
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下的类。
? ? 注:如果要忽略某个规则,那么加上@ArchIgnore就可以了,参考这里。
List-3
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.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
? ? 实际项目中,代码量较多,规范要做好,特别是人员流动是公司中很常见的。
(adsbygoogle = window.adsbygoogle || []).push({});