Maven引用坐标:
1
2
3
4
5
|
<
dependency
>
<
groupId
>org.tinygroup</
groupId
>
<
artifactId
>vfs</
artifactId
>
<
version
>0.0.12</
version
>
</
dependency
>
|
于是Apache VFS就进入了我的框架,功能也完全良好。但是在压力测试的时候,忽然发现有内存泄露问题,dump一下内存,进行分析之后,发现原来是Apache VFS 2.0惹得祸,看一看apache VFS已经好久没有升级了,跟踪了一下源码,发现有些地方,比较诡异,就是有时候进有时候不进,查之良久而不得,只好下决定把Apache VFS从里面拿掉,而拿掉之前,就需要有同样功能的东东支撑,不得已,花费2天时间写了一个VFS,功能比Apache的VFS少一些,但是够用了。然后花了一天时间,把原来Apache VFS的代码迁移到新的VFS之上,做此项迁移工作的小弟手指都木了,迁移完之后,再行测试,内存泄露问题不再存在。
好了,上面讲了前因,下面就介绍一下VFS的构成及实现:
模式提供者接口用于扩展各种文件来源,比如:本地文件,HTTP文件,FTP文件,Jar包文件,Zip文件等等,其接口声明如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/**
* 模式提供者接口
*/
public
interface
SchemaProvider {
/**
* 是否匹配
*
* @param resource
* @return 如果返回true,表示此提供者可以处理,返回false表示不能处理
*/
boolean
isMatch(String resource);
/**
* 返回处理的模式
*
* @return
*/
String getSchema();
/**
* 解析资源,并返回文件对象
*
* @param resource
* @return
*/
FileObject resolver(String resource);
}
|
文件对象FileObject,是对文件的访问的统一接口,它的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public
interface
FileObject {
String getFileName();
// 返回文件名
String getPath();
// 返回路径
String getAbsolutePath();
// 返回绝对路径
String getExtName();
// 返回扩展名
boolean
isExist();
// 是否存在
long
getSize();
// 返回文件大小
InputStream getInputStream();
//返回输入流
boolean
isFolder();
// 返回是否是目录,如果是目录,则getInputStream无效。
FileObject getParent();
// 返回上级文件
void
setParent(FileObject fileObject);
// 设置上级文件
List<FileObject> getChildren();
// 返回下级文件列表
FileObject getChild(String fileName);
//获取参数名称指定的fileobject
long
getLastModifiedTime();
// 返回修改时间
SchemaProvider getSchemaProvider();
//返回模式提供者
boolean
isInPackage();
//是否是包文件
URL getURL();
//返回url
OutputStream getOutputStream();
//返回输出流
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
/**
* 虚拟文件系统
*/
public
class
VFS {
static
Map<String, FileObject> fileObjectCacheMap =
new
ConcurrentHashMap<String, FileObject>();
static
Map<String, Long> fileModifyTimeMap =
new
ConcurrentHashMap<String, Long>();
static
final
Map<String, SchemaProvider> schemaProviderMap =
new
HashMap<String, SchemaProvider>();
private
static
String defaultSchema =
"file:"
;
static
{
addSchemaProvider(
new
JarSchemaProvider());
addSchemaProvider(
new
ZipSchemaProvider());
addSchemaProvider(
new
FileSchemaProvider());
addSchemaProvider(
new
HttpSchemaProvider());
addSchemaProvider(
new
HttpsSchemaProvider());
addSchemaProvider(
new
FtpSchemaProvider());
addSchemaProvider(
new
JBossVfsSchemaProvider());
}
/**
* 构建函数私有化
*/
private
VFS() {
}
/**
* 清空Cache
*/
public
static
final
void
clearCache() {
fileObjectCacheMap.clear();
}
/**
* 添加新的模式提供者
*
* @param schemaProvider
*/
public
static
final
void
addSchemaProvider(SchemaProvider schemaProvider) {
schemaProviderMap.put(schemaProvider.getSchema(), schemaProvider);
}
/**
* 设置默认模式提供者
*
* @param schema
*/
public
static
final
void
setDefaultSchemaProvider(String schema) {
defaultSchema = schema;
}
/**
* 返回指定的模式提供者
*
* @param schema
* @return
*/
public
static
final
SchemaProvider getSchemaProvider(String schema) {
return
schemaProviderMap.get(schema);
}
/**
* 解析文件
*
* @param resource
* @return
*/
public
static
FileObject resolveFile(String resource) {
FileObject fileObject = fileObjectCacheMap.get(resource);
if
(fileObject !=
null
) {
long
oldTime = fileModifyTimeMap.get(resource);
long
newTime = fileObject.getLastModifiedTime();
if
(oldTime == newTime) {
return
fileObject;
}
}
try
{
resource = URLDecoder.decode(resource,
"UTF-8"
);
}
catch
(UnsupportedEncodingException e) {
// 如果出错也不用管,忽略之
}
SchemaProvider schemaProvider = schemaProviderMap.get(defaultSchema);
for
(SchemaProvider provider : schemaProviderMap.values()) {
if
(provider.isMatch(resource)) {
schemaProvider = provider;
break
;
}
}
fileObject = schemaProvider.resolver(resource);
fileObjectCacheMap.put(resource, fileObject);
fileModifyTimeMap.put(resource, fileObject.getLastModifiedTime());
return
fileObject;
}
/**
* 解析URL
*
* @param url
* @return
*/
public
static
FileObject resolveURL(URL url) {
return
resolveFile(url.getPath());
}
}
|
可以看到,他实际上是一个工具类,提供了若干个静态方法,以供使用,因此,为了便于扩展,此类也没有加final,考虑到添加模式提供者一般是加载时单线程处理的,因此,为了提高效率,也没有增加线程安全相关的处理。
主体就是这三个类,接下来就是FileObject和SchemaProvider的扩展了,从下面的代码可以看到,默认已经添加了Jar文件支持,Zip文件支持,本地文件支持,Http资源支持,Ftp支持,JBossVfsSchema支持。
1
2
3
4
5
6
7
8
9
|
static
{
addSchemaProvider(
new
JarSchemaProvider());
addSchemaProvider(
new
ZipSchemaProvider());
addSchemaProvider(
new
FileSchemaProvider());
addSchemaProvider(
new
HttpSchemaProvider());
addSchemaProvider(
new
HttpsSchemaProvider());
addSchemaProvider(
new
FtpSchemaProvider());
addSchemaProvider(
new
JBossVfsSchemaProvider());
}
|
编写下面两行程序:
1
2
|
FileObject fileObject= VFS.resolveFile(
"src/main/java/org/tinygroup/vfs"
);
FileUtils.printFileObject(fileObject);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
absolutePath:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs
extName:null
fileName:vfs
inputsteam:null
path:
provider:org.tinygroup.vfs.impl.FileSchemaProvider@296672d6
size:0
children:[E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\FileObject.java, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\impl, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\SchemaProvider.java, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\util, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\VFS.java]
parent:null
exist:true
folder:true
url:file:E:/SVN/tinyorg-code/trunk/Sources/framework/base/vfs/src/main/java/org/tinygroup/vfs
------------------
absolutePath:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\FileObject.java
extName:java
fileName:FileObject.java
inputsteam:java.io.BufferedInputStream@13ccb029
path:/FileObject.java
provider:org.tinygroup.vfs.impl.FileSchemaProvider@296672d6
size:2077
children:null
parent:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs
exist:true
folder:false
url:file:E:/SVN/tinyorg-code/trunk/Sources/framework/base/vfs/src/main/java/org/tinygroup/vfs/FileObject.java
------------------
下面还有许多文件......省略了
|
可以看到确实是已经成功解析本地文件。
解析Web页面也是很方便的,如下:
1
2
3
|
FileObject fileObject=VFS.resolveFile(
"http://my.oschina.net/tinyframework"
);
assertTrue(fileObject
instanceof
HttpFileObject);
FileUtils.printFileObject(fileObject);
|
由于所有的文件对象都实现了FileObject接口,所以,我们成功的对所有的文件进行了封装,构建了我们自己的VFS。