https://www.archunit.org/userguide/html/000_Index.html
https://mp.weixin.qq.com/s/5SK-jxR8RsQIDZsieFRsug
GUI:
Bytecode Viewer https://github.com/Konloch/bytecode-viewer
https://bitbucket.org/mstrobel/procyon/wiki/Java%20Decompiler
http://www.benf.org/other/cfr/
CFR will decompile modern Java features - Java 8 lambdas (pre and post Java beta 103 changes), Java 7 String switches etc, but is written entirely in Java 6.
https://bitbucket.org/mstrobel/procyon/wiki/Java%20Decompiler
https://stackoverflow.com/questions/26304492/decompiler-supporting-java-8
Decompiler supporting Java 8?
https://sling.apache.org/documentation/development/jsr-305.html
https://mp.weixin.qq.com/s/5SK-jxR8RsQIDZsieFRsug
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "cc.codeasy.example")
public class ArchRuleTest {
@Arch
public static final ArchRule layer_dependencies_are_respected = layeredArchitecture()
.layer("Controllers")
.definedBy("cc.codeasy.example.controller..")
.layer("Services")
.definedBy("cc.codeasy.example.service..")
.layer("Daos")
.definedBy("cc.codeasy.example.dao..")
.whereLayer("Controllers")
.mayNotBeAccessedByAnyLayer()
.whereLayer("Services")
.mayOnlyBeAccessedByLayers("Controllers", "Services")
.whereLayer("Daos")
.mayOnlyBeAccessedByLayers("Services");
}
上面的代码几乎是不言自明的——Contrller层不能被任何层访问,Service层只能被Controller和自己访问,Dao层只能被Service访问。这条规则保证了层间依赖的正确性。如果Controller调用了Dao,这个JUnit测试就会运行失败,如果你使用Maven或Gradle,这个失败会让整个构建失败,结合CI,就可以阻止破坏分层架构的代码被提交到代码库中,对架构起到了应有的保护作用。
- 轻量级且开源,巧妙利用自动化单元测试,无缝和CI集成
- 规则和正式代码在一起,编写和修改随时都可以,且容易为每个工程的规则按需定制
- 简单且灵活的DSL,容易读懂,它看上去更像是配置而不是代码,但你需要灵活扩展的时候,可以实现很多的钩子,这时候它作为代码的优势又能体现出来了
@ArchTest
public static final ArchRule model_not_depend_on_others = classes()
.that()
.resideInAnyPackage("..model..")
.should()
.onlyDependOnClassesThat()
.resideInAnyPackage("..model..", "java..");
上面这个规则可以保证,model层要足够的技术无关,比如不能依赖Spring里的Utils等。
@ArchTest
public static final ArchRule service_impl_should_not_be_accessed = classes()
.that()
.haveSimpleNameEndingWith("ServiceImpl")
.should()
.onlyBeAccessed()
.byAnyPackage()
.because("should use IoC to access a interface instead of access a implementation.");
上面这个规则保障服务只能通过接口访问,不能之间访问实现类,这可以保障面向接口编程。
@ArchTest
public static final ArchRule controllers_should_not_dependend_each_other = SlicesRuleDefinition
.slices()
.matching(".controller.(**)")
.should()
.notDependOnEachOther();
上面这个规则可以保障Controller之间不互相访问,一般来说,Dao也符合这个规则。
slices是一个很强大的功能,它把类分成多个组,可以用它保障组之间没有环装依赖:
@ArchTest
public static final ArchRule services_should_not_have_cyclic_dependencies = SlicesRuleDefinition
.slices()
.matching(".service.(**)")
.should()
.beFreeOfCycles();
这种分组规则可以自定义,比如:
.slices().assignedFrom(new SliceAssignment() {
@Override
public String getDescription() {
return "myter slice";
}
@Override
public SliceIdentifier getIdentifierOf(JavaClass javaClass) {
if(javaClass.getPackageName().endsWith("Service")) {
return SliceIdentifier.of("service");
}
return SliceIdentifier.of("other");
}
)
这个规则在检查DDD的聚合根之间不能相互引用上非常有用。
还可以检查命名规则:
@ArchTest
public static final ArchRule servcie_interface_naming_conventions = classes()
.that()
.resideInAPackage("..service.")
.should()
.beInterfaces()
.andShould()
.haveSimpleNameEndingWith
GUI:
Bytecode Viewer https://github.com/Konloch/bytecode-viewer
https://bitbucket.org/mstrobel/procyon/wiki/Java%20Decompiler
- Bytecode Viewer is an open source Java decompilation, disassembly, and debugging suite by @Konloch. It can produce decompiled sources from several modern Java decompilers, including Procyon, CFR, and FernFlower.
http://www.benf.org/other/cfr/
CFR will decompile modern Java features - Java 8 lambdas (pre and post Java beta 103 changes), Java 7 String switches etc, but is written entirely in Java 6.
https://bitbucket.org/mstrobel/procyon/wiki/Java%20Decompiler
https://stackoverflow.com/questions/26304492/decompiler-supporting-java-8
Decompiler supporting Java 8?
The annotations used within Sling are based on the JSR-305 which is dormant since 2012. Nevertheless those annotations are understood by most of the tools and used by other Apache Projects like Apache OakOAK-37.
Due to the fact that Eclipse and FindBugs are interpreting annotations differently (Findbugs-1355). Sling only uses the following two different annotations which are supported by both tools:
javax.annotation.CheckForNull
javax.annotation.Nonnull
Annotations which support setting the default null semantics of return values and or parameters on a package level cannot be leveraged for that reason.
Eclipse since Juno supports null analysis based on any annotations. Those need to be enabled in Preferences->Java->Compiler->Errors/Warnings viaEnable annoation-based null analysis. Also the annotations need to be configured. For Sling those are
javax.annotation.CheckForNull
as 'Nullable' annotationjavax.annotation.Nonnull
as 'NonNull' annotation
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <version>3.0.0</version> <configuration> <visitors>InconsistentAnnotations,NoteUnconditionalParamDerefs,FindNullDeref,FindNullDerefsInvolvingNonShortCircuitEvaluation</visitors> </configuration> <executions> <execution> <id>run-findbugs-fornullchecks</id> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin>https://softdevbuilttolast.wordpress.com/2010/03/23/findbugs-making-your-code-easier-to-use-in-a-robust-way-with-nullability-annotations/
There are three scenarios when it comes to non-primitive values: you assume that values can’t be null, you assume that they can…or you assume nothing. FindBug provides us with three corresponding annotations:
@NonNull
, @CheckForNull
and @UnknownNullness
.