ArchUnit在代码检测方面的应用
Posted Dreamer who
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArchUnit在代码检测方面的应用相关的知识,希望对你有一定的参考价值。
1、为什么做代码检测功能
不管使用哪种语言,哪种框架,只要你编程,都会遇到些所谓的“坑”,只能靠代码检测工具自动发现这些问题,因为人脑或者是人并不总是最靠谱的。
也许你已经用了阿里的检查插件P3C,或者SonarQube、JArchitect、checkstyle或者findbugs等等工具来检测来避免,现在介绍一款java的基于字节码的我们可以自定义的,比较通用的检测工具。
2、ArchUnit
使用这个工具的目的,其实想利用java反射的方式,基于字节码解析的方式,随心所欲的来检测代码,我们可以随便加规则。
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. It does so by analyzing given Java bytecode, importing all classes into a Java code structure.
ArchUnit 在代码风格检测方面做的比较好,比如检测包或类、层之间的依赖是否符合公司的规范。
3、示例
目前项目中的使用:
maven-enforcer-plugin做的代码规范检测,按照官方文档http://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html,自定义规则。
需要引入ArchUnit 自己码方式增加规则,我们得改变maven-enforcer-plugin的执行周期:<phase>process-classes</phase>,不然,还没编译成字节码就执行了,就完全失去了检测机会。
1、检测第三方包中的内部工具类不让开发者使用
private static final ArchRule NOT_RESIDE_IN_BRAVE_INTERNAL_PACKAGE = ArchRuleDefinition.noClasses().that()
.resideInAnyPackage("com.xxx..", "com.yyy..")
.should()
.accessClassesThat()
.resideInAnyPackage("brave.internal..", "com.beust.jcommander.internal..")
.because("第三方jar包的工具类不能使用");
第三方包brave.internal和com.beust.jcommander.internal里的一些工具类,开发经常依赖IDE自动导入,而其实想用的是jdk中的类。
检测结果显示:
Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in any package ['com.xxx..', 'com.yyy..'] should access classes that reside in any package ['brave.internal..', 'com.beust.jcommander.internal..'], because 第三方jar包的工具类不能使用' was violated (1 times):
Method <com.xxx.YYYYY.submitScore(java.lang.String, java.lang.Long, int)> calls method <com.beust.jcommander.internal.Sets.newHashSet()> in (YYYYY.java:520)
2、threadlocal工具类被非controller的业务类大量使用,等改造并行执行业务时,导致一些参数传递失败(其他部门因为这个事情损失了几百万好像是)
比如我们的工具类是:ccom.xxx.common.web.util.CommonContextUtil
private static final ArchRule CONTEXT_UTIL_RULE = ArchRuleDefinition.classes().that()
.areNotAnnotatedWith("org.springframework.web.bind.annotation.RestController")
.and().areNotAnnotatedWith("org.springframework.stereotype.Controller")
.should(new ArchCondition<JavaClass>("only be accessed in controller class")
@Override
public void check(JavaClass item, ConditionEvents events)
Set<String> excludes = SUB_RULE_EXCLUDE.get(RuleTypeEnum.ContextUtilSubRule.name());
if (Objects.nonNull(excludes) && !excludes.isEmpty() && excludes.contains(item.getSimpleName()))
return;
java.util.Optional<Dependency> first = item.getDirectDependenciesFromSelf().stream().filter(t -> t.getTargetClass().getName().equals("ccom.xxx.common.web.util.CommonContextUtil")).findFirst();
if (first.isPresent())
String message = String.format(
"class %s 用了ContextUtil类 , %s", item.getFullName(), first.get().getSourceCodeLocation());
events.add(SimpleConditionEvent.violated(item, message));
).because("ccom.xxx.common.web.util.CommonContextUtil 多线程切换有隐患, \\n 如果想忽略检查,可以配置如下示例 \\n" +
"<subRuleExclude>\\n" +
"\\t<ContextUtilSubRule>xx.java,yy.java</ContextUtilSubRule>\\n" +
"</subRuleExclude>");
上面的代码也能写成第一种示例的简化形式。
简单了写了下,要想使用,可以参考官方文档及demo示例和api。当然这种检测会增加maven编译的时间,我们可以利用git的一些自动触发工具,异步检测,报警到人或群也可以。
以上是关于ArchUnit在代码检测方面的应用的主要内容,如果未能解决你的问题,请参考以下文章