• 为指定 java 类生成 PlantUML puml文件工具( v2 )


    AttributePumlVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. @Getter
    5. @Setter
    6. public class AttributePumlVO implements Serializable {
    7. /**
    8. * 属性名称
    9. */
    10. private String name;
    11. /**
    12. * 属性类型
    13. */
    14. private Class type;
    15. @Override
    16. public String toString() {
    17. return "\ticon_hammer " + this.name + ": " + this.type.getSimpleName() + "\n";
    18. }
    19. }

    ClassPumlGenerate.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import org.reflections.Reflections;
    4. import java.io.*;
    5. import java.lang.reflect.Field;
    6. import java.lang.reflect.Method;
    7. import java.net.JarURLConnection;
    8. import java.net.URL;
    9. import java.net.URLDecoder;
    10. import java.util.*;
    11. import java.util.jar.JarEntry;
    12. import java.util.jar.JarFile;
    13. @Getter
    14. @Setter
    15. public class ClassPumlGenerate {
    16. private Set classIdentifiers = new HashSet<>();
    17. private List classPumlList = new ArrayList<>();
    18. private static final Set JDK_METHOD_NAMES = new HashSet<>();
    19. private static final Set JDK_CLASS_NAMES = new HashSet<>();
    20. private static final Set JDK_ATTRIBUTE_NAMES = new HashSet<>();
    21. static {
    22. JDK_METHOD_NAMES.add( "wait" );
    23. JDK_METHOD_NAMES.add( "equals" );
    24. JDK_METHOD_NAMES.add( "toString" );
    25. JDK_METHOD_NAMES.add( "hashCode" );
    26. JDK_METHOD_NAMES.add( "notify" );
    27. JDK_METHOD_NAMES.add( "notifyAll" );
    28. JDK_METHOD_NAMES.add( "finalize" );
    29. JDK_CLASS_NAMES.add( "boolean" );
    30. JDK_CLASS_NAMES.add( "void" );
    31. JDK_CLASS_NAMES.add( "int" );
    32. JDK_CLASS_NAMES.add( "long" );
    33. JDK_CLASS_NAMES.add( "float" );
    34. JDK_CLASS_NAMES.add( "byte" );
    35. JDK_CLASS_NAMES.add( "double" );
    36. JDK_CLASS_NAMES.add( "short" );
    37. JDK_CLASS_NAMES.add( "[Ljava.lang.Object;" );
    38. JDK_CLASS_NAMES.add( "[B" );
    39. JDK_CLASS_NAMES.add( "[Ljava.lang.String;" );
    40. JDK_ATTRIBUTE_NAMES.add( "serialVersionUID" );
    41. }
    42. public void generatePumlForPackage( String packagePath,String outputPath,boolean ignoreInterface,boolean ignoreProperties ){
    43. BufferedWriter writer = null;
    44. try {
    45. writer = new BufferedWriter(new FileWriter( outputPath ));
    46. this.classPumlList = new ArrayList<>();
    47. List> clazzList = this.getClasses(packagePath);
    48. for( Class clazz:clazzList ){
    49. this.generate( clazz,ignoreInterface,ignoreProperties);
    50. }
    51. writer.write( "@startuml\r\n" );
    52. writer.write( "!define icon_hammer \r\n" );
    53. writer.write( "!define icon_cube \r\n" );
    54. writer.write( "skinparam Class {\r\n" );
    55. writer.write( "\tBackgroundColor #d3dcef/white\r\n" );
    56. writer.write( "}\r\n" );
    57. for( ClassPumlVO classPuml:classPumlList ){
    58. writer.write( classPuml.toString() );
    59. }
    60. writer.write( "@enduml\r\n" );
    61. } catch (Exception e) {
    62. } finally {
    63. if (writer != null) {
    64. try {
    65. writer.close();
    66. } catch (Exception e) {
    67. }
    68. }
    69. }
    70. }
    71. public void generatePuml( Class clazz,String outputPath,boolean ignoreInterface,boolean ignoreProperties ){
    72. BufferedWriter writer = null;
    73. try {
    74. writer = new BufferedWriter(new FileWriter( outputPath ));
    75. this.classPumlList = new ArrayList<>();
    76. this.generate( clazz,ignoreInterface,ignoreProperties);
    77. writer.write( "@startuml\r\n" );
    78. writer.write( "!define icon_hammer \r\n" );
    79. writer.write( "!define icon_cube \r\n" );
    80. writer.write( "skinparam Class {\r\n" );
    81. writer.write( "\tBackgroundColor #d3dcef/white\r\n" );
    82. writer.write( "}\r\n" );
    83. for( ClassPumlVO classPuml:this.classPumlList ){
    84. writer.write( classPuml.toString() );
    85. }
    86. writer.write( "@enduml\r\n" );
    87. } catch (Exception e) {
    88. } finally {
    89. if (writer != null) {
    90. try {
    91. writer.close();
    92. } catch (Exception e) {
    93. }
    94. }
    95. }
    96. }
    97. private void generate( Class clazz,boolean ignoreInterface,boolean ignoreProperties ){
    98. this.generate_inner( clazz,ignoreInterface,ignoreProperties );
    99. }
    100. public static void main(String[] args) {
    101. System.out.println( "xxxx$xxx".contains( "$" ) );
    102. }
    103. private void generate_inner(Class clazz,boolean ignoreInterface,boolean ignoreProperties) {
    104. boolean handleImplementClassList = false;
    105. // 只处理 class 和 interface
    106. if( clazz.isEnum() ){
    107. return;
    108. }
    109. String simpleClassName = clazz.getSimpleName();
    110. if( simpleClassName.toLowerCase( ).endsWith( "properties" ) && ignoreProperties ){
    111. return;
    112. }
    113. // 防止重复处理
    114. String classIdentifier = clazz.isInterface() + " " + simpleClassName;
    115. if( this.classIdentifiers.contains( classIdentifier ) ){
    116. return;
    117. }
    118. String longClassName = clazz.getName();
    119. // 对jdk 以及框架类非业务的class 忽略处理
    120. if( longClassName.startsWith( "org." ) ||
    121. longClassName.startsWith( "java." ) ||
    122. longClassName.startsWith( "sun." ) ||
    123. longClassName.startsWith( "com.alibaba.fastjson." ) ||
    124. longClassName.startsWith( "tk.mybatis." ) ||
    125. longClassName.startsWith( "javax." )){
    126. return;
    127. }
    128. if( JDK_CLASS_NAMES.contains( longClassName ) ){
    129. return;
    130. }
    131. this.classIdentifiers.add( classIdentifier );
    132. if( clazz.isInterface() ){
    133. if( ignoreInterface ){
    134. this.generate_inner_4ImplementClassList( clazz,ignoreInterface,ignoreProperties );
    135. return;
    136. }else {
    137. handleImplementClassList = true;
    138. }
    139. }
    140. ClassPumlVO classPuml = new ClassPumlVO();
    141. classPuml.setShortName( simpleClassName );
    142. classPuml.setLongName( clazz.getName() );
    143. classPuml.setInterface( clazz.isInterface() );
    144. this.classPumlList.add( classPuml );
    145. // 获取该类直接声明的属性
    146. Field[] fields = clazz.getDeclaredFields();
    147. if( fields != null && fields.length > 0 ){
    148. List attributePumlList = new ArrayList<>();
    149. for( Field field:fields ){
    150. String fieldName = field.getName();
    151. if( JDK_ATTRIBUTE_NAMES.contains( fieldName ) ){
    152. continue;
    153. }
    154. Class fieldType = field.getType();
    155. if( fieldType != null && "org.slf4j.Logger".equals( fieldType.getName() ) ){
    156. continue;
    157. }
    158. AttributePumlVO attributePuml = new AttributePumlVO();
    159. attributePuml.setName( fieldName );
    160. attributePuml.setType( fieldType );
    161. attributePumlList.add( attributePuml );
    162. // 对该属性类型对应的 class 进行递归处理
    163. this.generate_inner( field.getType(),ignoreInterface,ignoreProperties );
    164. }
    165. classPuml.setAttributePumlList( attributePumlList );
    166. }
    167. // 获取该类直接声明的方法
    168. Method[] methods = clazz.getDeclaredMethods();
    169. if( methods != null && methods.length > 0 ){
    170. List methodPumlList = new ArrayList<>();
    171. for( Method method:methods ){
    172. String methodName = method.getName();
    173. if( JDK_METHOD_NAMES.contains( methodName ) ){
    174. continue;
    175. }
    176. if( methodName.contains( "$" ) ){
    177. continue;
    178. }
    179. MethodPumlVO methodPuml = new MethodPumlVO();
    180. methodPuml.setName( methodName );
    181. methodPuml.setMethod( method );
    182. methodPuml.setReturnType( method.getReturnType() );
    183. methodPumlList.add( methodPuml );
    184. // 对该方法的返回类型对应的 class 进行递归处理
    185. this.generate_inner( method.getReturnType(),ignoreInterface,ignoreProperties );
    186. }
    187. classPuml.setMethodPumlList( methodPumlList );
    188. }
    189. if( handleImplementClassList ){
    190. // 当前 clazz是接口,获取其全部的实现类,递归调用此方法
    191. this.generate_inner_4ImplementClassList(clazz,ignoreInterface,ignoreProperties);
    192. }
    193. }
    194. private void generate_inner_4ImplementClassList(Class clazz, boolean ignoreInterface, boolean ignoreProperties) {
    195. if( clazz.getSimpleName().toLowerCase().endsWith( "mapper" ) ){
    196. return;
    197. }
    198. List> implementClassList = this.getImplementClassList4CurrentPackage(clazz);
    199. if( implementClassList == null || implementClassList.size() == 0 ){
    200. return;
    201. }
    202. for( Class implementClass:implementClassList ){
    203. this.generate_inner( implementClass,ignoreInterface,ignoreProperties );
    204. }
    205. }
    206. private List> getImplementClassList4CurrentPackage(Class clazz){
    207. String servicePackage = clazz.getPackage().getName();
    208. Reflections reflections = new Reflections(servicePackage);
    209. Set> subTypes = reflections.getSubTypesOf( clazz );
    210. if( subTypes == null || subTypes.size() == 0 ){
    211. return new ArrayList<>( 0 );
    212. }
    213. return new ArrayList<>(subTypes);
    214. }
    215. private List> getClasses(String packageName){
    216. //第一个class类的集合
    217. List> classes = new ArrayList>();
    218. //是否循环迭代
    219. boolean recursive = true;
    220. //获取包的名字 并进行替换
    221. String packageDirName = packageName.replace('.', '/');
    222. //定义一个枚举的集合 并进行循环来处理这个目录下的things
    223. Enumeration dirs;
    224. try {
    225. dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
    226. //循环迭代下去
    227. while (dirs.hasMoreElements()){
    228. //获取下一个元素
    229. URL url = dirs.nextElement();
    230. //得到协议的名称
    231. String protocol = url.getProtocol();
    232. //如果是以文件的形式保存在服务器上
    233. if ("file".equals(protocol)) {
    234. //获取包的物理路径
    235. String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
    236. //以文件的方式扫描整个包下的文件 并添加到集合中
    237. this.findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
    238. } else if ("jar".equals(protocol)){
    239. //如果是jar包文件
    240. //定义一个JarFile
    241. JarFile jar;
    242. try {
    243. //获取jar
    244. jar = ((JarURLConnection) url.openConnection()).getJarFile();
    245. //从此jar包 得到一个枚举类
    246. Enumeration entries = jar.entries();
    247. //同样的进行循环迭代
    248. while (entries.hasMoreElements()) {
    249. //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
    250. JarEntry entry = entries.nextElement();
    251. String name = entry.getName();
    252. //如果是以/开头的
    253. if (name.charAt(0) == '/') {
    254. //获取后面的字符串
    255. name = name.substring(1);
    256. }
    257. //如果前半部分和定义的包名相同
    258. if (name.startsWith(packageDirName)) {
    259. int idx = name.lastIndexOf('/');
    260. //如果以"/"结尾 是一个包
    261. if (idx != -1) {
    262. //获取包名 把"/"替换成"."
    263. packageName = name.substring(0, idx).replace('/', '.');
    264. }
    265. //如果可以迭代下去 并且是一个包
    266. if ((idx != -1) || recursive){
    267. //如果是一个.class文件 而且不是目录
    268. if (name.endsWith(".class") && !entry.isDirectory()) {
    269. //去掉后面的".class" 获取真正的类名
    270. String className = name.substring(packageName.length() + 1, name.length() - 6);
    271. try {
    272. //添加到classes
    273. classes.add(Class.forName(packageName + '.' + className));
    274. } catch (ClassNotFoundException e) {
    275. }
    276. }
    277. }
    278. }
    279. }
    280. } catch (IOException e) {
    281. }
    282. }
    283. }
    284. } catch (IOException e) {
    285. }
    286. return classes;
    287. }
    288. /**
    289. * 以文件的形式来获取包下的所有Class
    290. * @param packageName
    291. * @param packagePath
    292. * @param recursive
    293. * @param classes
    294. */
    295. private void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List> classes){
    296. //获取此包的目录 建立一个File
    297. File dir = new File(packagePath);
    298. //如果不存在或者 也不是目录就直接返回
    299. if (!dir.exists() || !dir.isDirectory()) {
    300. return;
    301. }
    302. //如果存在 就获取包下的所有文件 包括目录
    303. File[] dirfiles = dir.listFiles(new FileFilter() {
    304. //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
    305. public boolean accept(File file) {
    306. return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
    307. }
    308. });
    309. //循环所有文件
    310. for (File file : dirfiles) {
    311. //如果是目录 则继续扫描
    312. if (file.isDirectory()) {
    313. findAndAddClassesInPackageByFile(packageName + "." + file.getName(),
    314. file.getAbsolutePath(),
    315. recursive,
    316. classes);
    317. }else {
    318. //如果是java类文件 去掉后面的.class 只留下类名
    319. String className = file.getName().substring(0, file.getName().length() - 6);
    320. try {
    321. //添加到集合中去
    322. classes.add(Class.forName(packageName + '.' + className));
    323. } catch (ClassNotFoundException e) {
    324. }
    325. }
    326. }
    327. }
    328. }

    ClassPumlVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. import java.util.List;
    5. @Getter
    6. @Setter
    7. public class ClassPumlVO implements Serializable {
    8. private boolean isInterface;
    9. private String longName;
    10. private String shortName;
    11. private List attributePumlList;
    12. private List methodPumlList;
    13. @Override
    14. public String toString() {
    15. StringBuilder sb = new StringBuilder("");
    16. if( this.isInterface ){
    17. sb.append( "interface" );
    18. }else {
    19. sb.append( "class" );
    20. }
    21. sb.append( " " );
    22. sb.append( this.shortName );
    23. // sb.append( this.longName );
    24. sb.append( " {\n" );
    25. if( this.attributePumlList != null && this.attributePumlList.size() > 0 ){
    26. for( AttributePumlVO attributePuml:this.attributePumlList ){
    27. sb.append( attributePuml.toString() );
    28. }
    29. }
    30. if( this.methodPumlList != null && this.methodPumlList.size() > 0 ){
    31. for( MethodPumlVO methodPuml:methodPumlList ){
    32. sb.append( methodPuml.toString() );
    33. }
    34. }
    35. sb.append( "}\n" );
    36. return sb.toString();
    37. }
    38. }

    MethodPumlVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. import java.lang.reflect.Method;
    5. @Getter
    6. @Setter
    7. public class MethodPumlVO implements Serializable {
    8. private String name;
    9. private Class returnType;
    10. private Method method;
    11. @Override
    12. public String toString() {
    13. return "\ticon_cube " + this.name + "(): " + this.returnType.getSimpleName() + "\n";
    14. }
    15. }

    使用示例:

    1. public static void main(String[] args) throws ClassNotFoundException, IOException {
    2. ClassPumlGenerate classPumlGenerate = new ClassPumlGenerate();
    3. Class clazz = XxxService.class;
    4. String outputPath = "C:\\E\\xxx\\xxx\\xxx\\xxx\\xxx-xxx-xxx\\src\\main\\resources\\puml\\xxx\\puml\\" + clazz.getSimpleName() + ".puml";
    5. classPumlGenerate.generatePuml( clazz,outputPath,true,true );
    6. }
    7. }

  • 相关阅读:
    记录一次时序数据库的实战测试
    介绍 dubbo-go 并在Mac上安装,完成一次自己定义的接口RPC调用
    轻量封装WebGPU渲染系统示例<21>- 3D呈现元胞自动机之生命游戏(源码)
    8.查询数据
    [附源码]java毕业设计医院管理系统
    Monaco Editor教程(十七):代码信息指示器CodeLens配置详解
    Java基础 引用数据类型String(字符串)
    人事管理系统
    微信支付v2
    java数据结构与算法刷题-----LeetCode28:实现 strStr()
  • 原文地址:https://blog.csdn.net/heshiyuan1406146854/article/details/134037276