Apache Commons VFS 2.10.0 配置参数详解
Apache Commons VFS 2.10.0 的 providers.xml 装配、缓存策略、FilesCache 实现、提供者注册与文件监听等全部配置参数的实测详解
Apache Commons VFS 2.10.0 配置参数详解
目标:梳理 Commons VFS 2.10.0 的全部配置入口与关键参数默认值,实测缓存策略、FilesCache 实现、提供者注册与文件监听参数的真实行为,已在 Ubuntu 24.04 实测通过。
一、环境信息
- 操作系统:Ubuntu 24.04.5 LTS
- 架构:x86_64
- JDK:OpenJDK 17.0.20.1(Temurin)
- 构建工具:Apache Maven 3.9.16
- 组件版本:Apache Commons VFS 2.10.0、commons-compress 1.27.1、commons-collections4 4.5.0-M3
- Maven 坐标:org.apache.commons:commons-vfs2:2.10.0
- 工作目录:/work/vfs-demo
实测环境确认:
java -version 2>&1 | head -1 && mvn -v | head -1
实测回显:
openjdk version "17.0.20.1" 2026-08-18
Apache Maven 3.9.16 (bbf2ae4d5949be9244c9c5cf4a904f1a27d2965a)
二、前置条件
- 已按《Apache Commons VFS 2.10.0 快速入门与集成示例》建好 /work/vfs-demo 工程。
- classpath 含 commons-vfs2 2.10.0 及其传递依赖;本文部分实验用到 commons-compress 与 commons-collections4。
- 本文配置对象为 StandardFileSystemManager(VFS.getManager() 返回的实际类型),所有实测基于该类。
三、配置入口与查看方式
3.1 VFS 的配置文件 providers.xml
与多数库不同,Commons VFS 有一份内置的提供者装配文件:jar 内的 org/apache/commons/vfs2/impl/providers.xml。VFS.getManager() 初始化时加载它,决定哪些 URI scheme 可用。导出查看:
cd /tmp && jar xf ~/.m2/repository/org/apache/commons/commons-vfs2/2.10.0/commons-vfs2-2.10.0.jar org/apache/commons/vfs2/impl/providers.xml
head -30 org/apache/commons/vfs2/impl/providers.xml
实测回显(输出节选,实测首尾):
<providers>
<default-provider class-name="org.apache.commons.vfs2.provider.url.UrlFileProvider">
</default-provider>
<provider class-name="org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider">
<scheme name="file"/>
</provider>
<provider class-name="org.apache.commons.vfs2.provider.zip.ZipFileProvider">
<scheme name="zip"/>
</provider>
<provider class-name="org.apache.commons.vfs2.provider.tar.TarFileProvider">
<scheme name="tar"/>
<if-available class-name="org.apache.commons.compress.archivers.tar.TarArchiveOutputStream"/>
</provider>
<provider class-name="org.apache.commons.vfs2.provider.bzip2.Bzip2FileProvider">
<scheme name="bz2"/>
<if-available class-name="org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream"/>
</provider>
<provider class-name="org.apache.commons.vfs2.provider.gzip.GzipFileProvider">
<scheme name="gz"/>
</provider>
<provider class-name="org.apache.commons.vfs2.provider.jar.JarFileProvider">
<scheme name="jar"/>
<scheme name="sar"/>
<scheme name="ear"/>
<scheme name="par"/>
<scheme name="ejb3"/>
<scheme name="war"/>
</provider>
验证结果:providers.xml 由 default-provider、provider(含 scheme 与 if-available 条件)、extension-map、mime-type-map 四类元素组成。
3.2 三条配置通道
| 通道 | 覆盖范围 | 生效方式 |
|---|---|---|
| providers.xml(jar 内置) | 提供者与 scheme 装配 | VFS.getManager() 初始化时自动加载 |
| StandardFileSystemManager API | 缓存策略、FilesCache、提供者注册、默认提供者 | new StandardFileSystemManager() 后、init() 前调用配置方法 |
| JVM 系统属性 | 编码等运行环境(-Dfile.encoding 等) | 启动参数 |
自定义 provider 可通过 addProvider 在代码层注册,不必改 providers.xml;需要彻底替换装配时,可在 classpath 根放同名 providers.xml 覆盖(同路径资源优先级高于 jar 内资源)。
四、参数总览
默认值实测(反射读取内置常量):
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import java.lang.reflect.Field;
public class DefaultsProbe {
public static void main(String[] a) throws Exception {
Field f = DefaultFileMonitor.class.getDeclaredField("DEFAULT_DELAY");
f.setAccessible(true);
System.out.println("DefaultFileMonitor.DEFAULT_DELAY = " + f.get(null));
Field s = org.apache.commons.vfs2.cache.LRUFilesCache.class.getDeclaredField("DEFAULT_LRU_SIZE");
s.setAccessible(true);
System.out.println("LRUFilesCache.DEFAULT_LRU_SIZE = " + s.get(null));
}
}
实测回显:
DefaultFileMonitor.DEFAULT_DELAY = PT1S
LRUFilesCache.DEFAULT_LRU_SIZE = 100000
关键参数默认值一览:
| 参数 | 默认值 | 说明 |
|---|---|---|
| 默认提供者 | UrlFileProvider | 未知 scheme 走 URL 处理 |
| 默认 FilesCache | DefaultFilesCache | 基于内存 Map,引用由管理器持有 |
| 默认 CacheStrategy | ON_RESOLVE | resolve 时刷新文件对象 |
| DefaultFileMonitor 扫描间隔 | PT1S(1000ms) | DEFAULT_DELAY 常量 |
| DefaultFileMonitor 递归 | true | 子目录一并监控 |
| LRUFilesCache 容量 | 100000 | DEFAULT_LRU_SIZE 常量 |
| RandomAccessMode | READ / READWRITE 两种 | 只读随机访问最常用 |
| 临时文件目录 | 系统临时目录(DefaultFileReplicator) | 启动时打 INFO 日志提示 |
五、核心参数分类详解
5.1 缓存策略 CacheStrategy
CacheStrategy 三个取值决定 VFS 何时自动刷新 FileObject 的缓存属性:
| 取值 | 语义 |
|---|---|
| MANUAL | 从不自动刷新,需调用 fo.refresh() |
| ON_RESOLVE | 仅在 resolveFile 时刷新(默认) |
| ON_CALL | 每次文件系统调用前刷新 |
用三个 StandardFileSystemManager 实例分别设置策略后实测(CacheProbe.java 核心逻辑:VFS 解析文件读一次大小,随后用 java.io.FileOutputStream 从外部改写同一文件,再读一次):
[1] MANUAL 首次读取=2 字节, 外部修改后=10 字节, refresh() 后=10 字节
[2] ON_RESOLVE 首次读取=2 字节, 外部修改后=10 字节, 重新 resolve 后=10 字节
[3] ON_CALL 首次读取=2 字节, 外部修改后=10 字节
[5] 默认管理器 首次读取=2 字节, 外部修改后=10 字节
验证结果与要点:
- 在 file 本地提供者上,三种策略对外部修改后的读取结果一致(均为 10 字节新值):本地提供者的属性读取直接落到底层 java.io.File,策略差异体现在缓存对象何时被 refresh,而不是属性读取本身。
- CacheStrategy 的实际意义在于多层场景(ram、zip 等自身有内部缓存的文件系统)与 FileObject 实例复用时的刷新时机;处理「外部进程改文件必须读到新值」的需求时,最稳妥的做法是显式 fo.refresh() 或重新 resolveFile。
- 默认管理器(VFS.getManager(),即 [5])行为与 ON_RESOLVE 一致。
5.2 FilesCache 实现选型
jar 内提供的 FilesCache 实现(缓存 resolveFile 得到的 FileObject):
| 实现 | 特点 | 依赖 |
|---|---|---|
| DefaultFilesCache | HashMap 结构,默认实现 | 无 |
| LRUFilesCache | 按访问序淘汰,上限内保活 | commons-collections4(optional,缺了直接 NoClassDefFoundError) |
| SoftRefFilesCache | 软引用,内存紧张时可被回收 | 无 |
| WeakRefFilesCache | 弱引用 | 无 |
| NullFilesCache | 不缓存,每次解析都新建 FileObject | 无 |
切换实测(CacheProbe 第 4 段):
StandardFileSystemManager sm4 = new StandardFileSystemManager();
sm4.setFilesCache(new LRUFilesCache(64));
sm4.setCacheStrategy(CacheStrategy.ON_CALL);
sm4.init();
[4] LRUFilesCache(64) + ON_CALL 读取 = LRU-DEMO 大小=8 字节
验证结果:setFilesCache + setCacheStrategy 在 init() 前调用即可生效,读写正常。
缺依赖时的真实报错(实测):
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections4/MapIterator
at org.apache.commons.vfs2.cache.LRUFilesCache.<init>(LRUFilesCache.java:69)
at CacheProbe.main(CacheProbe.java:45)
要点:LRUFilesCache 构造即可见依赖,务必在 pom 里显式加 commons-collections4(本文 4.5.0-M3)。
5.3 提供者注册参数 addProvider
addProvider(scheme, FileProvider) 可注册自定义 scheme 或覆盖内置 scheme。实测把 ram 提供者注册为新前缀 mem2(CustomSchemeDemo.java):
StandardFileSystemManager sm = new StandardFileSystemManager();
sm.addProvider("mem2", new org.apache.commons.vfs2.provider.ram.RamFileProvider());
sm.init();
运行实测回显:
[1] mem2 文件 mem2:///custom/hello.txt 大小=39 字节
内容 = 自定义 scheme mem2 复用 RAM 实现
[2] 再次解析 mem2:///custom/hello.txt 大小=39 字节
[3] 默认 VFS.getManager() 不识别 mem2: Could not find file with URI "mem2:///x.txt" because it is a relative path, and no base URI was provided.
验证结果与要点:
- addProvider 在 init() 前调用,注册后 mem2: 与 ram: 行为一致。
- 自定义注册只影响当前 StandardFileSystemManager 实例,VFS.getManager() 单例完全不受影响,适合做多租户隔离。
- addProvider(String[] schemes, FileProvider) 可一次注册多个前缀;setDefaultProvider(FileProvider) 替换兜底提供者。
5.4 DefaultFileMonitor 参数
DefaultFileMonitor(org.apache.commons.vfs2.impl 包)是后台线程轮询式文件监听器,可配置参数:
| 参数 | 方法 | 默认 | 说明 |
|---|---|---|---|
| 扫描间隔 | setDelay(long 毫秒) / setDelay(Duration) | PT1S | 轮询周期 |
| 递归监控 | setRecursive(boolean) | true | 子目录一并监控 |
| 监控对象 | addFile(FileObject) / removeFile(FileObject) | 无 | 支持 file/folder |
实测(MonitorDemo.java,delay=300ms,监控目录,随后创建/追加/删除目录下文件):
[0] 监控参数 delay=300ms recursive=true
[1] DefaultFileMonitor 已启动, 监控目录 /work/vfs-demo/monitor
[2] 启动观察期结束(上方若无事件输出, 说明启动时对已存在文件 pre.txt 不误报)
[监听] 创建: /work/vfs-demo/monitor/notes.txt
[监听] 修改: /work/vfs-demo/monitor/notes.txt
[监听] 删除: /work/vfs-demo/monitor/notes.txt
[3] DefaultFileMonitor 已停止
验证结果与要点:
- 三类事件 fileCreated/fileChanged/fileDeleted 在一次运行内全部触发。
- addFile 传入目录 + recursive 时,启动时已存在的文件不会触发创建事件(pre.txt 无误报);监控目录比监控单文件更能稳定捕获新建文件事件。
- 单文件模式下 addFile 时文件已存在,则后续只有修改与删除事件,不会补发创建事件(实测发现)。
- stop() 后线程退出;间隔越短感知越快但空转开销越大,常规 500ms 至 1000ms。
5.5 随机读参数 RandomAccessMode
FileContent.getRandomAccessContent(RandomAccessMode) 返回 RandomAccessContent(扩展了 DataInput/DataOutput)。RandomAccessMode 只有 READ 与 READWRITE 两个取值,随机读选 READ 防止误写。实测(RandomAccessDemo.java,内容 0123456789ABCDEFGHIJ):
[1] 打开随机读: 内容长度=21 字节, 初始指针=0
[2] seek(5) + readFully(5) = 56789, 指针移动到 10
[3] seek(0) + readFully(全部) = 0123456789ABCDEFGHIJ
[3a] readLine() 在 2.10.0 已废弃, 直接抛出: Deprecated
[4] seek(15) + read() = F (ASCII 70)
[5] 在文件末尾继续读抛出 EOFException
[6] RandomAccessContent 已关闭
验证结果与要点:
- seek/readFully/readUnsignedByte 正常;指针位置随读取推进。
- readLine() 在 2.10.0 已废弃且直接抛 UnsupportedOperationException,按字节或 readUTF 处理。
- 读到文件末尾再读抛 EOFException,与 java.io.DataInput 语义一致。
5.6 providers.xml 中的 scheme 清单
providers.xml 注册的全部 scheme 及其生效条件(依据 3.1 节导出的真实文件整理):
| scheme | 提供者 | 生效条件(if-available) |
|---|---|---|
| file | DefaultLocalFileProvider | 无条件 |
| ram | RamFileProvider | 无条件 |
| zip | ZipFileProvider | 无条件(纯 JDK 实现) |
| tar | TarFileProvider | commons-compress |
| bz2 | Bzip2FileProvider | commons-compress |
| gz | GzipFileProvider | 无条件 |
| tgz / tbz2 | TarFileProvider | gz/tar 或 bz2/tar 均可用 |
| jar、sar、ear、par、ejb3、war | JarFileProvider | 无条件 |
| tmp | TemporaryFileProvider | 无条件 |
| res | ResourceFileProvider | 无条件 |
| ftp / ftps | FtpFileProvider / FtpsFileProvider | commons-net |
| sftp | SftpFileProvider | jsch |
| http / https | Http5FileProvider / Http5sFileProvider | httpclient5 |
| http5 / http5s | Http5FileProvider / Http5sFileProvider | httpclient5 |
| http4 / http4s、http3 / http3s | Http4/Http3 系列 | httpclient4 / httpclient3 |
| webdav、webdav3、webdav4、webdav4s | Webdav 系列 | 对应 httpclient + jackrabbit-webdav |
| hdfs | HdfsFileProvider | hadoop |
实测可用性(ProvidersProbe,当前 classpath 只含 vfs2 及其传递依赖加 commons-compress):
file:///work/vfs-demo/quick/hello.txt => 解析成功, 目标状态: file
ram:///probe.txt => 解析成功, 目标状态: 不存在
zip:file:/work/vfs-demo/archives/demo.zip!/ => 解析成功, 目标状态: folder
tar:file:/work/vfs-demo/archives/bundle.tar!/ => 解析成功, 目标状态: folder
tgz:file:/work/vfs-demo/archives/bundle.tar.gz!/ => 解析成功, 目标状态: folder
jar:file:/work/vfs-demo/target/vfs-demo-1.0.0.jar!/ => 解析成功, 目标状态: folder
res:/probe.txt => 失败: Badly formed URI "res:/probe.txt".
tmp:/probe.txt => 解析成功, 目标状态: 不存在
ftp://192.168.1.62/probe.txt => 失败: Could not find file with URI "ftp://192.168.1.62/probe.txt" because it is a relative path, and no base URI was provided.
sftp://192.168.1.62/probe.txt => 失败: Could not find file with URI "sftp://192.168.1.62/probe.txt" because it is a relative path, and no base URI was provided.
http://192.168.1.62/probe.txt => 失败: Could not find file with URI "http://192.168.1.62/probe.txt" because it is a relative path, and no base URI was provided.
https://192.168.1.62/probe.txt => 失败: Could not find file with URI "https://192.168.1.62/probe.txt" because it is a relative path, and no base URI was provided.
webdav://192.168.1.62/probe.txt => 失败: Could not find file with URI "webdav://192.168.1.62/probe.txt" because it is a relative path, and no base URI was provided.
验证结果与要点:
- ftp/sftp/http/https/webdav 的 "relative path" 报错是提供者未安装的信号(if-available 条件不满足)。
- res: 要求 URI 指向真实存在的 classpath 资源,否则报 Badly formed URI;实测 res:/org/apache/commons/vfs2/impl/providers.xml 解析成功。
- tmp: 基于系统临时目录,解析任意路径都不报错但目标不存在。
六、生产环境参数模板
服务端初始化推荐模板(要点全部来自本文实测项):
import org.apache.commons.vfs2.CacheStrategy;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.cache.SoftRefFilesCache;
public class VfsFactory {
public static StandardFileSystemManager create() {
StandardFileSystemManager sm = new StandardFileSystemManager();
// 长驻服务:软引用缓存避免 FileObject 泄漏堆积
sm.setFilesCache(new SoftRefFilesCache());
// 外部进程会改文件:resolve 时刷新
sm.setCacheStrategy(CacheStrategy.ON_RESOLVE);
sm.init();
return sm;
}
}
配套启动参数与依赖清单:
java -Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -jar app.jar
- classpath 必需:commons-vfs2 2.10.0(自带 commons-logging 1.3.4、commons-lang3 3.17.0、commons-io 2.18.0)。
- 按需:commons-compress 1.27.1(tar/bz2)、commons-collections4 4.5.0-M3(LRUFilesCache)、commons-net、jsch、httpclient5(对应协议)。
- DefaultFileMonitor 生产间隔建议 1000ms;监听器内避免阻塞,重逻辑交给业务线程池。
七、常见问题(FAQ)
7.1 CacheStrategy 设成 MANUAL 后本地文件还能读到新值
正常。实测(5.1 节)本地提供者的属性读取实时落到 java.io.File,MANUAL 不会造成本地文件读到旧值;策略影响的是缓存对象刷新时机,多层文件系统(ram/zip 内部缓存)场景差异更明显。需要强一致时显式 refresh()。
7.2 LRUFilesCache 报 NoClassDefFoundError: org/apache/commons/collections4/MapIterator
commons-collections4 是 optional 依赖,Maven 不自动带入。显式引入 org.apache.commons:commons-collections4 即可。
7.3 解析 tar: 报 relative path, and no base URI was provided
tar/bz2/ftp/sftp/http 等提供者是 if-available 条件注册,对应依赖缺失时不安装。引入 commons-compress(tar/bz2)、commons-net(ftp)、jsch(sftp)、httpclient5(http/https)。
7.4 DefaultFileMonitor 不触发创建事件
两种常见原因:一是 addFile 时文件已存在(实测不会补发创建事件),改为监控父目录并开启 recursive;二是间隔过长,文件创建又删除发生在两次扫描之间。
7.5 如何查看当前环境实际注册了哪些 scheme
用 VFS.getManager().resolveFile 试探各 scheme(本文 ProvidersProbe 方式),或解包 commons-vfs2-2.10.0.jar 查看 org/apache/commons/vfs2/impl/providers.xml,结合 classpath 上的 optional 依赖判断 if-available 是否满足。
八、参考链接
- Apache Commons VFS 官网:https://commons.apache.org/proper/commons-vfs/
- VFS Javadoc(DefaultFileSystemManager):https://commons.apache.org/proper/commons-vfs/apidocs/org/apache/commons/vfs2/impl/DefaultFileSystemManager.html
- VFS Javadoc(CacheStrategy):https://commons.apache.org/proper/commons-vfs/apidocs/org/apache/commons/vfs2/CacheStrategy.html
- Maven Central 构件页:https://repo1.maven.org/maven2/org/apache/commons/commons-vfs2/
- commons-collections4 构件页:https://repo1.maven.org/maven2/org/apache/commons/commons-collections4/