java脑洞 效率工程利器-代码解析工具 lp-base-export

脑洞的由来

开发过程中经常遇到

  1. 将Controller导出成API文档
  2. 将枚举注释导出,用于数据库注释或者API文档注释
  3. 将持久化的DO的注释导出,用于数据库注释
  4. 将错误码导出,用于API文档注释

当前常见解决方案

  1. ctrl + C 和 ctrl + V 大法,武林无敌之技
  2. swagger
  3. apiggs

效率对比

  1. 只要不怕累死,没啥缺点,主要是耗时
  2. 只能针对Controller生成文档,对代码有入侵,而且需要额外开启服务端口,产线禁止
  3. 同样针对Controller生产文档,和swagger相比,apiggs是基于代码解析生成的静态文档,对代码无入侵,而且不需要开启web服务,更安全更简便

介绍 lp-base-export

  1. 支持源码java文件解析,能拿到注释
  2. 支持字节码class文件解析,字节码解析是拿不到注释的
  3. 生成静态文档,无需开启web服务
  4. 对项目无入侵,在项目外通过maven插件或者ant等工具启动任务
  5. 生成文档样式全自定义,通过mvel表达式来解析生成
  6. 能递归获取父类属性
  7. 突破代码解析的最后壁垒(泛型),支持泛型解析后的泛型注入
  8. 能配合lombok(字节码解析支持,源码解析不支持)

版本

日期版本号
2024-08-071.3.0.FINAL
2024-08-071.3.0-SPRING3.FINAL(针对spring3和jdk17的版本)

开始使用

方式一. 通过maven插件使用

1) 引入maven插件
<plugin>
				<groupId>io.github.wqr503</groupId>
				<artifactId>enum-export-plugin</artifactId>
				<version>1.3.0.FINAL</version>
				<configuration>
					<taskList>
						<task>
							<id>enumTask</id>
							<outPutDirection>${project.basedir}/export</outPutDirection>
							<!--                            <classPaths>-->
							<!--                                <classPath>${project.basedir}/target/classes</classPath>-->
							<!--                            </classPaths>-->
							<sourcePath>${project.basedir}/src/main/java</sourcePath>
							<dependencyPaths>
								<dependencyPath>${project.basedir}/target/lib</dependencyPath>
							</dependencyPaths>
							<logLevel>DEBUG</logLevel>
							<logParam>true</logParam>
							<mvlText>
								<![CDATA[
public interface CombinationEnum {
@foreach{entity : entityList}
    // @{entity.typeName}
    String @{entity.name} = "@if{entity.desc != null}@{entity.desc} : @end{}@foreach{data : entity.valueList}@if{data.fieldList.size() > 0}@{data.fieldList[0].value}@end{}@if{data.fieldList.size() <= 0}@{data.ordinal}@end{}:@{data.name}(@if{data.desc != null}@{data.desc}@end{}),@end{}";
@end{}
}
  ]]>
							</mvlText>
						</task>
					</taskList>
				</configuration>
			</plugin>

详情可看另一篇文章:
java脑洞 效率工程利器-代码解析maven插件 enum-export-plugin

方式二. 通过独立项目使用

1) jdk 要求 8+
2) 引入maven
<dependency>
  <groupId>io.github.wqr503</groupId>
  <artifactId>lp-base-export</artifactId>
  <version>1.3.0.FINAL</version>
</dependency>
3) 编写demo
1. 聚合输出,是指所有扫描出来的类聚合输出到一个文件里面,也就是所有扫描出来的类共用一个TableAttribute,TableAttribute中的getAttribute只会获取一次
new Exportor()
                // 输出地址
                .setBaseDir("D:\\lp-base-export\\export")
                // 源码路径地址(和字节码路径地址 二选一)
                //.setSourceJavaPath("D:\\lp-base-export\\src\\main\\java")
                // 字节码路径地址(源码路径地址 二选一)
                .setSourceClassPath("D:\\lp-base-export\\target\\classes")
                // 依赖包路径(可为空,没有依赖包则由于找不到Class则减少了扫描深度)
                .setDependencyPath("D:\\lp-base-export\\target\\lib")
                // 扫描的包路径(可为空)
                .setBasePackage("com.cn.lp.export")
                // 聚合输出的mvel表达式(和CombinationMvlPath 二选一)
//                .setCombinationMvlText(new CombinationMvlTexter() {
//                    @Override
//                    public String getCombinationMvelText() {
//                        return "测试输出:" +
//                                "//@{dto}\n";
//                    }
//                })
                // 聚合输出的mvel文件(和CombinationMvlText 二选一)
                .setCombinationMvlPath(new CombinationPather() {
                    @Override
                    public String getCombinationPath() {
                        return "D:\\lp-base-export\\export\\mvl\\dto.mvl";
                    }
                })
                // 具体输出文件名
                .setOutputCombinationFileName(new CombinationPather() {
                    @Override
                    public String getCombinationPath() {
                        return "EnumConstants.txt";
                    }
                })
                // 编程语言(语法糖解析)
                .setFormatter(LangFormatter.JAVA)
                // 扫描过滤器
                .addFilter(ClassFilterHelper.ofInclude(new Predicate<ScanClassInfo>() {
                    @Override
                    public boolean test(ScanClassInfo scanClassInfo) {
                        return true;
                    }
                }))
                // 构建提供给mvel的属性对象
                .setCreator(new TableAttributeCreator() {
                    @Override
                    public TableAttribute create() {
                        return new TableAttribute() {

                            private List<String> nameList = new ArrayList<>();

                            // 从扫描出来的对象中提取属性
                            @Override
                            public void putAttribute(ScanClassInfo classInfo, TypeFormatter typeFormatter) {
                                nameList.add(classInfo.getClassName());
                            }

                            // 输出给mvel的属性对象
                            @Override
                            public Map<String, Object> getAttribute() {
                                Map<String, Object> map = new HashMap<>();
                                map.put("nameList", nameList);
                                return map;
                            }
                        };
                    }
                })
                // 是否打印参数
                .setLogParam(true)
                // 设置打印等级 ERROR,WARN,INFO,DEBUG,TRACE
                .setLogLevel(Level.INFO)
                .combinationExportAll();

效果如下图:
image.png
image.png

2. 流水输出,是指所有扫描出来的类每个都会新建一个新的TableAttribute,根据TableAttribute中的getAttribute也会生成一个对应的文件,也就是扫描出3个类,就会有3个TableAttribute和3个对应生成的文件
new Exportor()
                // 输出地址
                .setBaseDir("D:\\lp-base-export\\export")
                // 源码路径地址(和字节码路径地址 二选一)
                //.setSourceJavaPath("D:\\lp-base-export\\src\\main\\java")
                // 字节码路径地址(源码路径地址 二选一)
                .setSourceClassPath("D:\\lp-base-export\\target\\classes")
                // 依赖包路径(可为空,没有依赖包则由于找不到Class则减少了扫描深度)
                .setDependencyPath("D:\\lp-base-export\\target\\lib")
                // 扫描的包路径(可为空)
                .setBasePackage("com.cn.lp.export")
                // 流水输出的mvel表达式(和MvlPath 二选一),可以不同对象对应不同mvel表达式
                .setMvlTexter(new MvlTexter() {
                    @Override
                    public String getMvelText(ScanClassInfo classInfo) {
                        return "测试输出:\n" +
                                "    @{name}\n";
                    }
                })
                // 流水输出的mvel文件(和MvlTexter 二选一),可以不同对象对应不同mvel文件
//                .setMvlPath(new Pather() {
//                    @Override
//                    public String getPath(ScanClassInfo classInfo) {
//                        return "D:\\lp-base-export\\export\\mvl\\dto.mvl";
//                    }
//                })
                // 流水输出文件名
                .setOutputFileName(new Pather() {
                    @Override
                    public String getPath(ScanClassInfo classInfo) {
                        return classInfo.getClassName().replace(".", "/") + "_Export.txt";
                    }
                })
                // 编程语言(语法糖解析)
                .setFormatter(LangFormatter.JAVA)
                // 扫描过滤器
                .addFilter(ClassFilterHelper.ofInclude(new Predicate<ScanClassInfo>() {
                    @Override
                    public boolean test(ScanClassInfo scanClassInfo) {
                        return true;
                    }
                }))
                // 构建提供给mvel的属性对象
                .setCreator(new TableAttributeCreator() {
                    @Override
                    public TableAttribute create() {
                        return new TableAttribute() {

                            private String name;

                            // 从扫描出来的对象中提取属性
                            @Override
                            public void putAttribute(ScanClassInfo classInfo, TypeFormatter typeFormatter) {
                                name = classInfo.getClassName();
                            }

                            // 输出给mvel的属性对象
                            @Override
                            public Map<String, Object> getAttribute() {
                                Map<String, Object> map = new HashMap<>();
                                map.put("name", name);
                                return map;
                            }
                        };
                    }
                })
                // 是否打印参数
                .setLogParam(true)
                // 设置打印等级 ERROR,WARN,INFO,DEBUG,TRACE
                .setLogLevel(Level.INFO)
                .exportAll();

效果如下图:
image.png
image.png

3. 对象描述

ScanClassInfo 字段描述
字段类型描述
classNameString类名(包含包路径)
simpleNameString文件名
sourceClassSourceClassclass对象
SourceClass 接口描述
字段类型描述
findAnnotation查找注解Optional<SourceAnnotation>
getAnnotationFieldMap获取注解字段 - 只有注解类才有值Map<String, SourceAnnotationField>
getMethodList获取方法列表List<SourceMethod>
getClassLoader获取当前加载的ClassLoaderClassLoader
getClassType获取类信息SourceType
filterSuperClass递归到最深查找是否有该父类Collection<SourceType>
filterInterface递归到最深查找是否有该接口Collection<SourceType>
getEnumValueList获取枚举值List<SourceEnumValue>
getName获取类名String
getDesc获取描述String
getFieldMap获取字段列表Map<String, SourceField>
getAnnotationList获取注解列表List<SourceAnnotation>
SourceType接口描述
字段类型描述
findAnnotation查找注解Optional<SourceAnnotation>
getInterfaceList获取接口列表Collection<SourceType>
getSuperClass获取父类SourceType
isArray是否数组 []boolean
isEnum是否枚举boolean
isFinal是否不可变boolean
isAbstract是否抽象boolean
isAnnotationType是否注解boolean
isInterface是否接口boolean
isHasParameterizedType是否泛型boolean
getClassLoader获取ClassLoaderClassLoader
getActualTypeArgumentMap获取泛型MapMap<String, SourceType>
getTypeName获取类路径(包含包路径)String
getSimpleName获取类名String
getFullName获取包含泛型的名字String
SourceAnnotation接口描述
字段类型描述
getName获取名字String
getFieldMap获取字段列表String
getClassLoader获取ClassLoaderClassLoader
getSourceType获取类信息SourceType
getTypeName获取类路径String
SourceAnnotationField接口描述
字段类型描述
getReturnType获取返回值SourceType
getValue获取值Object
getName获取名字String
getDefaultValue获取默认值Object
getDesc获取描述String
SourceField接口描述
字段类型描述
getModifier获取修饰符String
isVolatile是否volatile修饰boolean
isStatic是否static修饰boolean
isFinal是否final修饰boolean
findAnnotation查找某个注解Optional<SourceAnnotation>
getAnnotationList获取注解列表List<SourceAnnotation>
getFieldType获取字段类型SourceType
getDesc获取描述String
getName获取字段名String
SourceEnumValue接口描述
字段类型描述
getOrdinal获取原序号Integer
getName获取枚举名String
getFieldList获取值列表List<SourceEnumValueField>
getDesc获取描述String
SourceEnumValueField接口描述
字段类型描述
getName获取名字String
getValue获取值Object
SourceParam接口描述
字段类型描述
getAnnotationList获取注解列表List<SourceAnnotation>
getParamType获取类信息SourceType
getName获取入参名String

4. 拓展使用

  1. 配合Ant或者Gradle外部启动lp-base-export程序,分析当前项目代码再生成报告
  2. 封装成maven插件,通过maven插件调用,比如apiggs和enum-export-plugin

结语

起初该项目用于提取代码中枚举的注释然后批量生成接口注释,也可用于提取持久化对象字段的注释批量生成Mysql的字段的注释。最早是用Doclet来解析源码的,然后用ant来启动项目,其实这是相当不方便的,然后最近通过Javaparser来替代Doclet解析源码,减少了项目对jdk的依赖,同时通过提前暴露的思路攻克了泛型注入的问题,使得最后生成的对象更完整和更准确,同时也支持了内部类的解析。项目是独立项目,所以对分析的项目是零入侵,通过maven,gradle,ant等工具拉起项目,最后生成报告到指定目录。该项目我自己一直在使用,如果遇到问题可留言,我会单独联系给你解决。

如果这篇文章对你有帮助请给个star
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值