当前位置: 代码网 > it编程>编程语言>Java > 一文带你掌握Java如何自动化生成目录结构文档

一文带你掌握Java如何自动化生成目录结构文档

2026年01月15日 Java 我要评论
前言在开源世界的版图上,目录结构就是项目的“城市沙盘”。第一次推开仓库大门的开发者,往往先扫一眼根目录下的文件夹与文件,再决定是留下来深耕,还是转身离开。可这份“沙

前言

在开源世界的版图上,目录结构就是项目的“城市沙盘”。第一次推开仓库大门的开发者,往往先扫一眼根目录下的文件夹与文件,再决定是留下来深耕,还是转身离开。可这份“沙盘”却常年处于失修状态:手写 readme 的树状图随着迭代迅速过时,新加的模块没人补录,删掉的包路径依旧躺在文档里“诈尸”。于是,维护者陷入“改代码五分钟,改文档半小时”的泥沼,贡献者则在“代码与描述对不上”的迷宫中兜圈。更严重的是,当项目被 maven central、github package 收录,或进入企业内网供上千人复用时,一份过期目录说明直接拉低了整个工程的可信度。开发者开始质疑:如果连目录都懒得同步,核心业务逻辑是否也藏着暗坑?这种“文档债务”像复利一样滚雪球,最终把技术品牌拖进信任黑洞。

痛点催生需求,需求催生轮子。java 生态历来“万物皆库”,把文档生成做成一个可嵌入的 jar,比任何外部脚本都更轻、更快、更可移植。思路可以拆成三步:第一步,用 java.nio.file.files递归扫描,把路径、文件大小、最后修改时间一次性收进内存,形成一棵“物理树”;第二步,借 javaparser 扫描 src/main/java,把包名、类名映射成“语义树”,再与物理树按路径合并,让目录节点瞬间拥有“做什么”的业务标签;第三步,把整棵树渲染成 markdown,直接写回 docs/structure.md,maven 只需在 compile 阶段挂一条 exec:java 指令,就能在每次打包前完成自动更新。

今天,我们全文围绕一个真实的 spring boot 单体仓库演示,每一步都是可拷贝的 .java 文件,不依赖任何外部 cli。读完这篇,你将把“目录说明”从手工清单变成编译产物,像 class 文件一样,随构建永远保持最新。开源项目的门面,从此只靠 java 代码自己说话。

一、来看看优秀的项目

本节我们来看看一些在gitee和github中的优秀项目案例,来看看他们的项目目录又是怎么规划和展示的,抛砖引玉,通过本节的说明,让大家了解在正规的项目中都是如何来规划这些目录的。让人一看就知道他的规划非常清晰。

1、开源项目介绍

首先来看看在社区中常见的对于开源项目的目录介绍文本,其主要内容如下所示:

# 项目名称
项目描述...
## 📁 项目结构
```text
.
├── src/
│   ├── main/
│   │   ├── java/com/example/
│   │   │   ├── controller/
│   │   │   ├── service/
│   │   │   ├── repository/
│   │   │   └── model/
│   │   └── resources/
│   │       ├── application.properties
│   │       └── static/
│   └── test/
│       └── java/com/example/
├── pom.xml
├── readme.md
└── .gitignore
```
*注:此目录树自动生成,更新于 $(date)*

2、开源项目目录分析

可以看到这就是一个比较标准的springboot项目。在项目中使用maven进行项目管理和构建。版本控制使用的是git这个软件。在源码方面,我们可以看到正式工程目录main和测试工程目录test。而在main中,由同时包含控制层、业务层、模型层和响应的资源目录信息。应该说这是一个比较完整的开源项目目录说明文件了。但是美中不足的地方是,这些文本的描述缺乏对项目目录的中文详细说明,还有不同的目录和文件,没有进行相应的标注。虽然中文的标注可以修改,但是由标注会让工程看起来更直观和清晰。

二、纯java原生实现

本节将重点详细介绍如何使用java原生来进行实现。包括常用的配置说明,默认的配置设置,如何去解析命令中的参数和如何加载自定义的配置。通过本节的描述,大家都能够掌握如何使用java进行原生的目录解释实现。

1、java常用配置说明

在java中,尤其是后端的web项目中,我们通常会包含以下的目录,比如总体的工程目录、src表示源码目录、java表示java源代码目录、resources表示资源目录、controller表示控制层目录、service表示业务层目录、repository表示数据访问层目录等等。我们在进行工程模板的设置时往往可以自由进行设置,这里我们使用java来预定义一些常用的配置。这里默认采用静态块的模式进行加载设置:

static {
    // 初始化常用目录描述
    directory_descriptions.put("src", "源代码目录");
    directory_descriptions.put("src/main", "主代码目录");
    directory_descriptions.put("src/main/java", "java源代码");
    directory_descriptions.put("src/main/resources", "资源文件");
    directory_descriptions.put("src/test", "测试代码目录");
    directory_descriptions.put("src/test/java", "java测试代码");
    directory_descriptions.put("src/test/resources", "测试资源文件");
    directory_descriptions.put("com", "java包目录");
    directory_descriptions.put("controller", "控制器层");
    directory_descriptions.put("service", "服务层");
    directory_descriptions.put("service/impl", "服务实现层");
    directory_descriptions.put("repository", "数据访问层");
    directory_descriptions.put("dao", "数据访问对象层");
    directory_descriptions.put("entity", "实体类");
    directory_descriptions.put("model", "模型层");
    directory_descriptions.put("dto", "数据传输对象");
    directory_descriptions.put("vo", "视图对象");
    directory_descriptions.put("config", "配置类");
    directory_descriptions.put("util", "工具类");
    directory_descriptions.put("utils", "工具类");
    directory_descriptions.put("constant", "常量类");
    directory_descriptions.put("exception", "异常处理");
    directory_descriptions.put("interceptor", "拦截器");
    directory_descriptions.put("filter", "过滤器");
    directory_descriptions.put("aop", "面向切面编程");
    directory_descriptions.put("aspect", "切面类");
    directory_descriptions.put("handler", "处理器");
    directory_descriptions.put("listener", "监听器");
    directory_descriptions.put("task", "定时任务");
    directory_descriptions.put("job", "定时任务");
    // 初始化常用文件描述
    file_descriptions.put("pom.xml", "maven项目配置文件");
    file_descriptions.put("build.gradle", "gradle构建文件");
    file_descriptions.put("gradle.properties", "gradle属性文件");
    file_descriptions.put("settings.gradle", "gradle设置文件");
    file_descriptions.put("package.json", "node.js包配置文件");
    file_descriptions.put("package-lock.json", "node.js包锁定文件");
    file_descriptions.put("yarn.lock", "yarn包锁定文件");
    file_descriptions.put("readme.md", "项目说明文档");
    file_descriptions.put("readme-cn.md", "中文项目说明文档");
    file_descriptions.put("readme_en.md", "英文项目说明文档");
    file_descriptions.put("contributing.md", "贡献指南");
    file_descriptions.put("changelog.md", "更新日志");
    file_descriptions.put("license", "许可证文件");
    file_descriptions.put(".gitignore", "git忽略文件配置");
    file_descriptions.put(".gitattributes", "git属性配置");
    file_descriptions.put(".editorconfig", "编辑器配置");
    file_descriptions.put("docker-compose.yml", "docker compose配置");
    file_descriptions.put("dockerfile", "docker构建文件");
    file_descriptions.put("docker-compose.yaml", "docker compose配置");
    file_descriptions.put("application.properties", "spring boot配置文件");
    file_descriptions.put("application.yml", "spring boot配置文件(yaml)");
    file_descriptions.put("application.yaml", "spring boot配置文件(yaml)");
    file_descriptions.put("application-dev.properties", "spring boot开发环境配置");
    file_descriptions.put("application-prod.properties", "spring boot生产环境配置");
    file_descriptions.put("application-test.properties", "spring boot测试环境配置");
    file_descriptions.put("bootstrap.properties", "spring cloud启动配置");
    file_descriptions.put("bootstrap.yml", "spring cloud启动配置(yaml)");
    file_descriptions.put("logback-spring.xml", "logback日志配置");
    file_descriptions.put("logback.xml", "logback日志配置");
    file_descriptions.put("log4j2.xml", "log4j2日志配置");
    file_descriptions.put("log4j.properties", "log4j配置文件");
}

在上面的常用配置中,大家可以根据自己项目的实际情况进行设置,将一些目录删除或者替换掉即可。

2、默认配置

讲完了java中的常用配置,下面我们为了控制在生成目录时能欧控制输出的内容和处理条件。在这里我们需要定义一些常用的参数。参数说明如下:

序号参数名说明
1private static final set<string> ignored_dirs需要忽略的目录
2private static final set<string> ignored_files需要忽略的文件扩展名
3private static final map<string, string> directory_descriptions 目录描述映射(可以扩展这个映射来为特定目录添加描述)
4private static final map<string, string> file_descriptions文件描述映射
5private static string configfile 配置文件路径
6private static int maxdepth最大层级限制
7private static boolean showfiles是否显示文件
8private static boolean showdescriptions 是否显示描述
9private static boolean dirsonly 是否只显示目录

示例代码如下:

// 需要忽略的目录
private static final set<string> ignored_dirs = new hashset<>(arrays.aslist(
        ".git", ".idea", "target", "build", "out", "node_modules",
        ".gradle", ".settings", "bin", "dist", "logs",
        ".vscode", ".history", "__pycache__", ".metadata"
));
    
// 需要忽略的文件扩展名
private static final set<string> ignored_files = new hashset<>(arrays.aslist(
        ".class", ".jar", ".war", ".iml", ".project", ".classpath",
        ".log", ".tmp", ".cache", ".lock", ".swp", ".swo", ".pyc"
));

其它更多的参数就在此不详细列出,感兴趣的朋友可以留言或者下载链接的内容。

3、解析命令参数

这里我们使用命令行参数来进行统一运行,为了能在运行命令时传入相关参数,因此需要对命令行参数进行解析,解析的逻辑和过程我们不做过多的设计。仅涉及相关参数的读取,解析方法如一下代码:

/**
* -解析命令行参数
*/
private static map<string, string> parseargs(string[] args) {
     map<string, string> params = new hashmap<>();
        
        for (int i = 0; i < args.length; i++) {
            string arg = args[i];
            if (arg.startswith("--")) {
                int eqindex = arg.indexof('=');
                if (eqindex > 0) {
                    string key = arg.substring(2, eqindex);
                    string value = arg.substring(eqindex + 1);
                    params.put(key, value);
                } else if (i + 1 < args.length && !args[i + 1].startswith("--")) {
                    string key = arg.substring(2);
                    params.put(key, args[++i]);
                }
            } else if (i == 0 && !arg.startswith("--")) {
                params.put("path", arg);
            } else if (i == 1 && !arg.startswith("--")) {
                params.put("output", arg);
            }
    }
    return params;
}

可以看到,对命令行的参数进行解析的方法也比较简单。

4、加载自定义配置

为了方便用户可以自定义的进行配置的修改,我们不仅可以在程序中加载默认的参数,同时也可以加载外部的配置文件,当我们在运行的时候,就可以动态的修改参数也不需要修改代码,这样程序的扩展性就更强了。

/**
 * *加载自定义配置文件
 */
private static void loadcustomconfig() {
        file config = new file(configfile);
        if (config.exists()) {
            try {
                properties props = new properties();
                props.load(files.newinputstream(config.topath()));
                
                // 加载忽略目录
                string ignoreddirs = props.getproperty("ignored.dirs");
                if (ignoreddirs != null) {
                    collections.addall(ignored_dirs, ignoreddirs.split(","));
                }
                
                // 加载忽略文件
                string ignoredfiles = props.getproperty("ignored.files");
                if (ignoredfiles != null) {
                    collections.addall(ignored_files, ignoredfiles.split(","));
                }
                
                // 加载目录描述
                for (string key : props.stringpropertynames()) {
                    if (key.startswith("dir.")) {
                        directory_descriptions.put(key.substring(4), props.getproperty(key));
                    } else if (key.startswith("file.")) {
                        file_descriptions.put(key.substring(5), props.getproperty(key));
                    }
                }
                
                system.out.println("📋 已加载配置文件: " + configfile);
            } catch (ioexception e) {
                system.err.println("⚠️  无法加载配置文件: " + configfile);
            }
        }
}

5、生成目录树

在做了上述的功能之后,接下来我们就可以进行目录树的生成,为了实现在java中的层次展示,这里需要使用递归的方式进行调用。入口函数如下:

 /**
  * -生成目录树
  */
private static void generatetree(file node, string prefix, boolean islast, int depth, stringbuilder tree) {
        // 检查深限制
        if (depth > maxdepth) {
            return;
        }
        // 跳过忽略的文件和目录
        if (shouldignore(node, depth)) {
            return;
        }
        string name = node.getname().isempty() ? "." : node.getname();
        // 构建节点行
        stringbuilder line = new stringbuilder();
        line.append(prefix).append(islast ? "└── " : "├── ").append(name);
        // 添加描述(如果启用)
        if (showdescriptions) {
            string description = getdescription(node, depth);
            if (!description.isempty()) {
                line.append(" ").append(description);
            }
        }
        tree.append(line).append("\n");
        // 如果是文件且不需要递归,或者只显示目录且当前是文件,则返回
        if (!node.isdirectory() || (dirsonly && !node.isdirectory())) {
            return;
        }
        // 如果是目录,递归处理子节点
        file[] children = getsortedchildren(node);
        for (int i = 0; i < children.length; i++) {
            string newprefix = prefix + (islast ? "    " : "│   ");
            boolean childislast = (i == children.length - 1);
            generatetree(children[i], newprefix, childislast, depth + 1, tree);
        }
}

篇幅有限,还有很多代码无法全部呈现。如果需要完整的代码,可以从下载相应的源码(同时包含最简单的配置文件示例)。

三、成果展示

本节将重点展示一下,如何对当前项目工程和其他工程目录进行目录的输出。使用命令行的方式进行程序输出。让大家可以快速的对目标工程进行目录的输出和介绍。

1、生成目录树实现

在工程中,不仅要将目录树生成出来,同时还要生成一个可以解释说明的表格。在markdown中可以直接使用以下代码进行生成,非常方便,当然这里也是考虑递归生成的,源代码如下:

/**
 *- 收集描述信息生成表格
 */
private static void collectdescriptions(file node, string path, stringbuilder result) {
        if (shouldignore(node, 0)) {
            return;
        }
        string name = node.getname();
        string fullpath = path.isempty() ? name : path + "/" + name;
        if (node.isdirectory()) {
            // 添加目录描述到表格
            string description = getdescription(node, 0);
            if (!description.isempty()) {
                result.append("| `").append(fullpath).append("/` | ").append(description).append(" |\n");
            }
            // 递归处理子目录
            file[] children = node.listfiles();
            if (children != null) {
                for (file child : children) {
                    if (child.isdirectory() && !shouldignore(child, 0)) {
                        collectdescriptions(child, fullpath, result);
                    }
                }
        }
    }
}

2、生成当前工程目录

默认情况下生成的是当前的工程目录。因此我们可以直接在类中直接运行main方法,运行后会直接在工程跟目录下生成一个md文件,同时在控制台中可以看到以下输出:

打开文件夹,可以看到以下内容:

3、生成其它工程目录

如果想在一个工程中为其它的工程目录生成目录结构说明,并且进行相关目录的设置说明,就可以直接调用命令行参数来进行设置参数即可。在eclipse中可以在命令中输入以下命令进行运行,参数添加方式如下:

这里将命令行参数参数贴出来:

--path=../blueengine --output=diy_project_tree_blueengine.md --depth=10

这个命令的意思是给本工程同目录下的blueengine项目生成指定文件名的文件,路径深度为10层。程序运行后可以看到以下内容:

最后来看看生成的目录格式如下:

内容基本是符合我们的生成预期的。到此,我们的自定义目录生成并输出功能结束。

四、总结

本文主要对纯java实现工程的目录自定义输出进行介绍,文章详细介绍了开源项目为什么需要进行目录输入,然后详细介绍了纯java的原生实现的核心函数,最后介绍了两种不同的模式,通过给当前工程生成目录说明和生成其它工程目录。

以上就是一文带你掌握java如何自动化生成目录结构文档的详细内容,更多关于java生成目录结构的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com