如何自动生成简单的 Mermaid
类图
背景
我在 [Java] 一些类图 这篇文章里画了一些类图,但是那些类图是我手动生成的,虽说画图的过程有利于加深自己的理解,但是查看各个类/接口的信息毕竟比较麻烦,如果可以把生成类图的过程自动化,那么就可以大大提升画类图的效率了。
本文展示了我自己写的可以生成简单类图的 java
代码。文中展示了用它生成的以下类的类图
ArrayList
LinkedList
HashMap
/LinkedHashMap
/ConcurrentHashMap
java.util.JumboEnumSet
和java.util.RegularEnumSet
(它们是EnumSet
仅有的子类)java.util.concurrent.ThreadPoolExecutor
代码
我写了些代码,可以自动生成简单的 mermaid
类图。 请将以下代码保存为 ClassDiagramGenerator.java
⬇️
java
import java.lang.reflect.AccessFlag;
import java.util.*;
public class ClassDiagramGenerator {
private final Map<Class<?>, RealizationRelation> realizationRelations = new HashMap<>();
private final Map<Class<?>, InheritanceRelation> 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 generateHeader() {
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 (inheritanceRelations.containsKey(c)) {
System.out.printf("%s <|-- %s%n", inheritanceRelations.get(c).superNode().getSimpleName(), c.getSimpleName());
}
if (realizationRelations.containsKey(c)) {
String type = c.isInterface() ? "<|--" : "<|..";
realizationRelations.get(c).interfaceList().forEach(item -> {
System.out.printf("%s %s %s%n", item.getSimpleName(), type, c.getSimpleName());
});
}
});
generateSpecialClassAnnotation();
}
/**
* This method generated 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, new InheritanceRelation(currClass, superclass));
}
private void analyzeInterfaces(Class<?> currClass) {
Class<?>[] interfaces = currClass.getInterfaces();
for (Class<?> item : interfaces) {
analyzeHierarchy(item);
}
var interfaceList = Arrays.stream(interfaces).toList();
realizationRelations.put(currClass, new RealizationRelation(currClass, interfaceList));
}
}
/**
* A record class to hold inheritance relation
*/
record InheritanceRelation(Class<?> currNode, Class<?> superNode) {
}
/**
* A record class to hold realization relation
*/
record RealizationRelation(Class<?> currNode, List<Class<?>> interfaceList) {
}
用以下命令可以编译 ClassDiagramGenerator.java
bash
javac ClassDiagramGenerator.java
注意事项
请注意,以上代码生成的类图中,不包含任何泛型信息,而且自动生成的类图中也不展示任何字段/方法。
例子
下面举一些例子,说明 ClassDiagramGenerator.java
的用法。
例 1
: 生成 ArrayList
的类图
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.ArrayList'
运行结果是 Markdown
格式的,掘金文档的编辑区是支持相关格式的,具体的结果如下
运行结果
classDiagram
AbstractCollection <|-- AbstractList
List <|.. AbstractList
AbstractList <|-- ArrayList
List <|.. ArrayList
RandomAccess <|.. ArrayList
Cloneable <|.. ArrayList
Serializable <|.. ArrayList
Iterable <|-- Collection
Collection <|-- SequencedCollection
Collection <|.. AbstractCollection
SequencedCollection <|-- List
<> AbstractList
<> AbstractCollection
<> RandomAccess
<> Iterable
<> Collection
<> SequencedCollection
<> List
<> Cloneable
<> Serializable
在上图中的类名/接口名 | 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
的类图
bash
java ClassDiagramGenerator 'java.util.LinkedList'
运行结果是 Markdown
格式的,展示如下
运行结果
classDiagram
Queue <|-- Deque
SequencedCollection <|-- Deque
AbstractCollection <|-- AbstractList
List <|.. AbstractList
Iterable <|-- Collection
Collection <|-- SequencedCollection
AbstractList <|-- AbstractSequentialList
Collection <|-- Queue
AbstractSequentialList <|-- LinkedList
List <|.. LinkedList
Deque <|.. LinkedList
Cloneable <|.. LinkedList
Serializable <|.. LinkedList
Collection <|.. AbstractCollection
SequencedCollection <|-- List
<> AbstractList
<> AbstractSequentialList
<> AbstractCollection
<> Deque
<> Iterable
<> Collection
<> SequencedCollection
<> Queue
<> List
<> Cloneable
<> Serializable
在上图中的类名/接口名 | 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.Serializable |
例 3
: 生成 HashMap
/LinkedHashMap
/ConcurrentHashMap
的类图
这个例子举得不太好,因为 HashMap
是 LinkedHashMap
的 super
class
,所以 LinkedHashMap
的类图中一定会展示 HashMap
。
bash
java ClassDiagramGenerator 'java.util.HashMap' 'java.util.LinkedHashMap' 'java.util.concurrent.ConcurrentHashMap'
运行结果是 Markdown
格式的,展示如下
运行结果
classDiagram
AbstractMap <|-- HashMap
Map <|.. HashMap
Cloneable <|.. HashMap
Serializable <|.. HashMap
HashMap <|-- LinkedHashMap
SequencedMap <|.. LinkedHashMap
Map <|.. AbstractMap
AbstractMap <|-- ConcurrentHashMap
ConcurrentMap <|.. ConcurrentHashMap
Serializable <|.. ConcurrentHashMap
Map <|-- SequencedMap
Map <|-- ConcurrentMap
<> AbstractMap
<> Map
<> SequencedMap
<> ConcurrentMap
<> Cloneable
<> Serializable
在上图中的类名/接口名 | 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 |
例 4
: 生成 java.util.JumboEnumSet
和 java.util.RegularEnumSet
的类图
bash
java ClassDiagramGenerator 'java.util.JumboEnumSet' 'java.util.RegularEnumSet'
运行结果是 Markdown
格式的,展示如下
运行结果
classDiagram
Collection <|-- Set
AbstractSet <|-- EnumSet
Cloneable <|.. EnumSet
Serializable <|.. EnumSet
Iterable <|-- Collection
EnumSet <|-- JumboEnumSet
AbstractCollection <|-- AbstractSet
Set <|.. AbstractSet
Collection <|.. AbstractCollection
EnumSet <|-- RegularEnumSet
<> EnumSet
<> AbstractSet
<> AbstractCollection
<> Set
<> Iterable
<> Collection
<> Cloneable
<> Serializable
在上图中的类名/接口名 | 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 |
例 5
: 生成 ThreadPoolExecutor
的类图
bash
java ClassDiagramGenerator 'java.util.concurrent.ThreadPoolExecutor'
运行结果是 Markdown
格式的,展示如下
运行结果
classDiagram
Executor <|-- ExecutorService
AutoCloseable <|-- ExecutorService
AbstractExecutorService <|-- ThreadPoolExecutor
ExecutorService <|.. AbstractExecutorService
<> AbstractExecutorService
<> AutoCloseable
<> ExecutorService
<> Executor
在上图中的类名/接口名 | 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 |