一文详解Java如何自动生成简单的Mermaid类图
目录
- 背景
- 代码
- 例子
- 例1: 生成ArrayList的类图
- 例2: 生成LinkedList的类图
- 例3: 生成Java.util.Arrays$ArrayList的类图
- 例4: 生成List/Set/Deque的类图
- 例5: 生成HashMap/LinkedHashMap/ConcurrentHashMap的类图
- 例6: 生成java.util.ImmutableCollections$Set12和java.util.ImmutableCollections$SetN的类图
- 例7: 生成java.util.JumboEnumSet和java.util.RegularEnumSet的类图
- 例8: 生成ThreadPoolExecutor的类图
背景
虽说手动生成类图的过程有利于加深自己的理解,但是查看各个类/接口的信息毕竟比较麻烦,如果可以把生成类图的过程自动化,就可以大大提升画类图的效率了。
本文展示了我自己写的可以生成简单类图的 java 代码。文中展示了用它生成的以下类的类图
ArrayListLinkedListjava.util.Arrays$ArrayList(Arrays.asList(...)方法返回的是它的实例)List/Set/DequeHashMap/LinkedHashMap/ConcurrentHashMapSet12/SetN(Set.of(...)方法返回的是它们的实例)java.util.JumboEnumSet和java.util.RegularEnumSet(EnumSet.of(...)方法返回的是它们的实例)java.util.concurrent.ThreadPoolExecutor
代码
我写了如下的 java 代码,它可以自动生成简单的 Mermaid 类图。 请将以下代码保存为 ClassDiagramGenerator.java
import java.lang.reflect.AccessFlag;
import java.util.*;
public class ClassDiagramGenerator {
private final Map<Class<?>, Class<?>[]> realizationRelations = new HashMap<>();
private final Map<Class<?>, Class<?>> inheritanceRelations = new HashMap<>();
private final Set<Class<?>> analyzedClasses = new HashSet<>();
public static void main(String[] args) {
ClassDiagramGenerator generator = new ClassDiagramGenerator();
generator.convert(args).forEach(generator::analyzeHierarchy);
generator.generateClassDiagram();
generator.generateNameMappingTable();
}
/**
* Convert class name to the corresponding class object
*
* @param classNames give class names
* @return a list that contains corresponding class objects
*/
private List<Class<?>> convert(String[] classNames) {
if (classNames.length == 0) {
String hint = "Please refer to below usage and specify at least ONE class name!";
String usage = "Usage: java ClassDiagramGenerator 'java.util.ArrayList' 'java.util.LinkedList'";
throw new IllegalArgumentException(String.join(System.lineSeparator(), hint, usage));
}
List<Class<?>> classList = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
classList.add(clazz);
} catch (ClassNotFoundException e) {
String message = String.format("Class with name=%s can't be found, please check!", className);
throw new RuntimeException(message);
}
}
return classList;
}
/**
* Generate header for Mermaid class diagram
*/
private void generateHeajavascriptder() {
System.out.println("```mermaid");
System.out.println("classDiagram");
System.out.println();
}
/**
* Generate footer for Mermaid class diagram
*/
private void generateFooter() {
System.out.println("```");
}
/**
* Generate main content in Mermaid class diagram
*/
private void generateClassDiagram() {
generateHeader();
doGenerateClassDiagram();
generateFooter();
}
/**
* Generate a Markdown table that contains name mapping
*/
private void generateNameMappingTable() {
System.out.println();
System.out.println("| 在上图中的类名/接口名 | `Fully Qualified Name` |");
System.out.println("| --- | --- |");
Map<String, String> classNames = new TreeMap<>();
analyzedClasses.forEach(c -> {
String simpleName = c.getSimpleName();
if (classNames.containsKey(simpleName)) {
String prevName = classNames.get(simpleName);
String currName = c.getName();
String message = String.format("Duplicated simple class name detected! (%s and %s have the same simple name)", prevName, currName);
throw new IllegalArgumentException(message);
}
classNames.put(simpleName, c.getName());
});
classNames.forEach((simpleName, name) -> {
String row = String.format("| `%s` | `%s` |", simpleName, name);
System.out.println(row);
});
}
private void doGenerateClassDiagram() {
analyzedClasses.forEach(c -> {
if http://www.devze.com(inheritanceRelations.containsKey(c)) {
System.out.printf("%s <|-- %s%n", inheritanceRelations.get(c).getSimpleName(), c.getSimpleName());
}
if (realizationRelations.containsKey(c)) {
String type = c.isInterface() ? "<|--" : "<|..";
Arrays.stream(realizationRelations.get(c)).forEach(item -> {
System.out.printf("%s %s %s%n", item.getSimpleName(), type, c.getSimpleName());
});
}
});
generateSpecialClassAnnotation();
}
/**
* This method generates annotation for
* 1. Abstract classes
* 2. Interfaces
*/
private void generateSpecialClassAnnotation() {
Set<Class<?>> abstractClasses = new LinkedHashSet<>();
Set<Class<?>> interfaces = new LinkedHashSet<>();
analyzedClasses.forEach(c -> {
if (c.isInterface()) {
interfaces.add(c);
} else if (c.accessFlags().contains(AccessFlag.ABSTRACT)) {
abstractClasses.add(c);
}
});
if (!abstractClasses.isEmpty() || !interfaces.isEmpty()) {
System.out.println();
abstractClasses.forEach(c -> System.out.println("<<Abstract>> " + c.getSimpleName()));
interfaces.forEach(c -> System.out.println("<<interface>> " + c.getSimpleName()));
}
}
private void analyzeHierarchy(Class<?> currClass) {
if (!analyzedClasses.contains(currClass)) {
analyzeSuperClass(currClass);
analyzeInterfaces(currClass);
analyzedClasses.add(currClass);
}
}
private void analyzeSuperClass(Class<?> currClass) {
Class<?> superclass = currClass.getSuperclass();
if (superclass == null || superclass == Object.class) {
return;
}
analyzeHierarchy(superclass);
inheritanceRelations.put(currClass, superclass);
}
private void analyzeInterfaces(Class<?> currClass) {
Class<?>[] interfaces = currClass.getInterfaces();
for (Class<?> item : interfaces) {
analyzeHierarchy(item);
}
realizationRelations.put(currClass, interfaces);
}
}
用以下命令可以编译 ClassDiagramGenerator.java
javac ClassDiagramGenerator.java
注意事项
请注意,ClassDiagramGenerator 生成的类图中,不包含任何泛型信息,而且也不展示任何字段/方法。
例子
下面举一些例子,说明 ClassDiagramGenerator.java 的用法。
例1: 生成ArrayList的类图
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.ArrayList'
运行结果是 Markdown 格式的
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractCollection | java.util.AbstractCollection |
| AbstractList | java.util.AbstractList |
| ArrayList | java.util.ArrayList |
| Cloneable | java.lang.Cloneable |
| Collection | java.util.Collection |
| Iterable | java.lang.Iterable |
| List | java.util.List |
| RandoMACcess | java.util.RandomAccess |
| SequencedCollection | java.util.SequencedCollection |
| Serializable | java.io.Serializable |
变通的方式
如果您无法在掘金的文档中使用 Mermaid,那么也可以前往 mermaid.live/ 来查看对应的类图,我在 mermaid.live/ 看到的效果如下 (需要自行将结果复制到那个网页去)

例2: 生成LinkedList的类图
请运行以下命令以生成对应的内容
java ClassDiahttp://www.devze.comgramGenerator 'java.util.LinkedList'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractCollection | java.util.AbstractCollection |
| AbstractList | java.util.AbstractList |
| AbstractSequentialList | java.util.AbstractSequentialList |
| Cloneable | java.lang.Cloneable |
| Collection | java.util.Collection |
| Deque | java.util.Deque |
| Iterable | java.lang.Iterable |
| LinkedList | java.util.LinkedList |
| List | java.util.List |
| Queue | java.util.Queue |
| SequencedCollection | java.util.SequencedCollection |
| Serializable | java.io.Serializapythonble |
例3: 生成java.util.Arrays$ArrayList的类图
调用 Arrays.asList(...) 方法,得到的是 java.util.Arrays$ArrayList 的实例。 请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.Arrays$ArrayList'
运行结果是 Markdown 格式的,展示如下
运行结果
请注意下图中的 ArrayList 是 java.util.Arrays 中的一个嵌套类,而不是我们平时常用的 java.util.ArrayList

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractCollection | java.util.AbstractCollection |
| AbstractList | java.util.AbstractList |
| ArrayList | java.util.Arrays$ArrayList |
| Collection | java.util.Collection |
| Iterable | java.lang.Iterable |
| List | java.util.List |
| RandomAccess | java.util.RandomAccess |
| SequencedCollection | java.util.SequencedCollection |
| Serializable | java.io.Serializable |
例4: 生成List/Set/Deque的类图
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.List' 'java.util.Set' 'java.util.Deque'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| Collection | java.util.Collection |
| Deque | java.util.Deque |
| Iterable | java.lang.Iterable |
| List | java.util.List |
| Queue | java.util.Queue |
| SequencedCollection | java.util.SequencedCollection |
| Set | java.util.Set |
例5: 生成HashMap/LinkedHashMap/ConcurrentHashMap的类图
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.HashMap' 'java.util.LinkedHashMap' 'java.util.concurrent.ConcurrentjsHashMap'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractMap | java.util.AbstractMap |
| Cloneable | java.lang.Cloneable |
| ConcurrentHashMap | java.util.concurrent.ConcurrentHashMap |
| ConcurrentMap | java.util.concurrent.ConcurrentMap |
| HashMap | java.util.HashMap |
| LinkedHashMap | java.util.LinkedHashMap |
| Map | java.util.Map |
| SequencedMap | java.util.SequencedMap |
| Serializable | java.io.Serializable |
例6: 生成java.util.ImmutableCollections$Set12和java.util.ImmutableCollections$SetN的类图
当我们调用 Set.of(...) 方法时,会得到以下两个类的实例
java.util.ImmutableCollections$Set12java.util.ImmutableCollections$SetN
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.ImmutableCollections$Set12' 'java.util.ImmutableCollections$SetN'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractCollection | java.util.AbstractCollection |
| AbstractImmutableCollection | java.util.ImmutableCollections$AbstractImmutableCollection |
| AbstractImmutableSet | java.util.ImmutableCollections$AbstractImmutableSet |
| Collection | java.util.Collection |
| Iterable | java.lang.Iterable |
| Serializable | java.io.Serializable |
| Set | java.util.Set |
| Set12 | java.util.ImmutableCollections$Set12 |
| SetN | java.util.ImmutableCollections$SetN |
例7: 生成java.util.JumboEnumSet和java.util.RegularEnumSet的类图
当我们调用 EnumSet.of(...) 方法时,会得到以下两个类的实例
java.util.JumboEnumSetjava.util.RegularEnumSet
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.JumboEnumSet' 'java.util.RegularEnumSet'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractCollection | java.util.AbstractCollection |
| AbstractSet | java.util.AbstractSet |
| Cloneable | java.lang.Cloneable |
| Collection | java.util.Collection |
| EnumSet | java.util.EnumSet |
| Iterable | java.lang.Iterable |
| JumboEnumSet | java.util.JumboEnumSet |
| RegularEnumSet | java.util.RegularEnumSet |
| Serializable | java.io.Serializable |
| Set | java.util.Set |
例8: 生成ThreadPoolExecutor的类图
请运行以下命令以生成对应的内容
java ClassDiagramGenerator 'java.util.concurrent.ThreadPoolExecutor'
运行结果是 Markdown 格式的,展示如下
运行结果

| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
| AbstractExecutorService | java.util.concurrent.AbstractExecutorService |
| AutoCloseable | java.lang.AutoCloseable |
| Executor | java.util.concurrent.Executor |
| ExecutorService | java.util.concurrent.ExecutorService |
| ThreadPoolExecutor | java.util.concurrent.ThreadPoolExecutor |
到此这篇关于一文详解Java如何自动生成简单的Mermaid类图的文章就介绍到这了,更多相关Java生成Mermaid类图内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论