Apache Commons VFS 2.10.0 配置参数详解

Apache Commons VFS 2.10.0 的 providers.xml 装配、缓存策略、FilesCache 实现、提供者注册与文件监听等全部配置参数的实测详解

Apache Commons VFSApache Commons VFS2026/10/15

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)

二、前置条件

  1. 已按《Apache Commons VFS 2.10.0 快速入门与集成示例》建好 /work/vfs-demo 工程。
  2. classpath 含 commons-vfs2 2.10.0 及其传递依赖;本文部分实验用到 commons-compress 与 commons-collections4。
  3. 本文配置对象为 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 处理
默认 FilesCacheDefaultFilesCache基于内存 Map,引用由管理器持有
默认 CacheStrategyON_RESOLVEresolve 时刷新文件对象
DefaultFileMonitor 扫描间隔PT1S(1000ms)DEFAULT_DELAY 常量
DefaultFileMonitor 递归true子目录一并监控
LRUFilesCache 容量100000DEFAULT_LRU_SIZE 常量
RandomAccessModeREAD / 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):

实现特点依赖
DefaultFilesCacheHashMap 结构,默认实现无
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)
fileDefaultLocalFileProvider无条件
ramRamFileProvider无条件
zipZipFileProvider无条件(纯 JDK 实现)
tarTarFileProvidercommons-compress
bz2Bzip2FileProvidercommons-compress
gzGzipFileProvider无条件
tgz / tbz2TarFileProvidergz/tar 或 bz2/tar 均可用
jar、sar、ear、par、ejb3、warJarFileProvider无条件
tmpTemporaryFileProvider无条件
resResourceFileProvider无条件
ftp / ftpsFtpFileProvider / FtpsFileProvidercommons-net
sftpSftpFileProviderjsch
http / httpsHttp5FileProvider / Http5sFileProviderhttpclient5
http5 / http5sHttp5FileProvider / Http5sFileProviderhttpclient5
http4 / http4s、http3 / http3sHttp4/Http3 系列httpclient4 / httpclient3
webdav、webdav3、webdav4、webdav4sWebdav 系列对应 httpclient + jackrabbit-webdav
hdfsHdfsFileProviderhadoop

实测可用性(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 是否满足。

八、参考链接