代码质量之Archunit的使用

注:开发的编辑器: 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"。

简单来说,它是代码格式、类之间的依赖关系检查工具。

  1. 进入官网
  2. 点击右上角的"User Gruide"
  3. 之后就可以看到它的英文教程文档。

1.pom.xml中加入依赖

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>

2.添加规则

直接上代码了,如下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 [email protected] 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下的类。

3.Demo

新建一个interface,名为DemoInterface,之后运行List-2,结果如下图1所示,由于DemoInterface是interface,类名以Interface结尾(List-2中定义不能以Interface结尾),未以I开头(List-2中定义要以I开头):

代码质量之Archunit的使用

图1 运行List-2后报错信息

4.官方的Archunit example

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

实际项目中,代码量较多,规范要做好,特别是人员流动是公司中很常见的。

代码质量之Archunit的使用

相关推荐