JAVA中查找类或资源文件


   根据语义的不同,查找类或资源的方式通常不太一样;Servlet语义下查找的类或资源必须在WEB根目录下;在URLClassloader语义下查找的类或资源在指定的URL中;
而java.io.File查找的类或资源特定于本地文件系统,File实例文件和目录路径名的抽象表示形式,并不能说明文件系统就存在对应的目录或文件,可以用exist方法断定文件是否存在;构造File实例的时候,需要传入代表路径的字符串,正如getPath的含义一样只是返回这个字符串本身;如果字符串表达的是相对路径,则使用它和文件系统交互之前,必须加入前缀,这里指工作目录由System.getProperty("user.dir")确定,由前缀和传入字符串共同确定文件的唯一性,还有一点工作要做。拼接上之后构成了能够表达绝对路径的字符串,虽然能够表达“绝对路径”的意思,不见得特定操作系统接受它,比如多了"..",有些操作系统可能自身内部处理这些细节,但是在相互之前,这种责任应该由请求者JVM担当,对于JVM来说必须将路径规范化,才能和操作系统交互
  getPath 返回构造File实例的传入字符串
  getAbsolutePath 返回绝对路径:去除传入字符串的不必要的“/”或文件分隔符
  getCanonicalPath 返回绝对路径:去除传入字符串的不必要的“、”或文件分隔符;同时去除多余的“.”或“..”;改返回值是文件系统的唯一标准表示
  如果传入path是相对的,则用System.getProperty("user.dir")合并得到完整路径
注意:File实例代表的是本地系统文件,如果文件存在,可以用FileOutputStream或FileInputStream流操作本地文件;但针对压缩文件内的文件,File实例的很多操作不能正确进行,必须使用java.util.zip压缩API操作,如果读取压缩文件中的文件(在Java中,称这些内置文件为“条目”即目录、文件),需进行解压之后判断。
  在web应用中,我们经常直接或间接用ServletContext来定位资源,servlet语义下,定位资源必须在webroot之下。 
getResourcePaths 传入的路径必须以"/"开始,返回相对于web root 或/WEB-INF/lib/xxx.jar/META-INF/resources
所有下级目录或文件:
For example, for a web application containing:
   /welcome.html
   /catalog/index.html
   /catalog/products.html
   /catalog/offers/books.html
   /catalog/offers/music.html
   /customer/login.jsp
   /WEB-INF/web.xml
   /WEB-INF/classes/com.acme.OrderServlet.class
   /WEB-INF/lib/catalog.jar!/META-INF/resources/catalog/moreOffers/books.html

getResourcePaths("/") would return {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}, and

getResourcePaths("/catalog/") would return {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/", "/catalog/moreOffers/"}.

  getResource 返回路径的单个URL,查找方式同上,先查找web root,找不到再查找/WEB-INF/lib/xxx.jar/META-INF/resource
  getResourceAsStream 实现代理到getResource,然后返回url.openStream

  基于java.lang.Class实例的资源查找
  getResource 返回单个URL。如果name以"/"开头,则代理给Classloader的是"/"之后的部分;否则代理的是当前Class的包名(以"/"代替".")+name
  getResourceAsStream 代理url.openStream

  基于ClassLoader资源查找 
  Classloader有父子关系,getResource或getResources使用委托机制由父Classloader查找,如果找不到调用findResource(s)。
  getResource 查找具有给定名称的资源返回单个URL
  getResources 查找所有给定名称的资源返回URL列表
  getResourceAsStream 代理url.openStream,返回读取指定资源的输入流
  findResources 由子类实现
  getSystemResource和getSystemResourceAsStream为静态方法,由系统类加载器加载。

  使用ClassLoader加载类之前必须定位类文件,跟定位资源文件如图片一样。这个定位如果使用java.io.File表达,也就是说特定于本地系统文件;而嵌套的Applet下载的class执行文件可以处于网络的任何位置,只要能够下载;无论怎么样,仅仅使用File实例,对于ClassLoader的实现来讲是不够的,必须支持各式各样的路径表示。URL”人如其名"
”统一资源定位“,JVM将使用java.net.URL表达资源路径而使用URLClassLoader进行类或资源查找。
  JVM使用委托机制查找类或资源文件,其中很有名气的是AppClassLoader和ExtClassLoader,他们都支持URL资源。比如 AppClassLoader指定java.class.path下的所有文件转换为URL;例如
D:\developTools\workspace\ims\build\classes;D:\developTools\workspace\ims\lib\extreme\extremecomponents-1.0.4.jar;将转换成
URL url1 = new URL("file","","D:\developTools\workspace\ims\build\classes");
URL url2 = new URL(("file","D:\developTools\workspace\ims\lib\extreme\extremecomponents-1.0.4.jar",");
URL[] urls = new URL[]{url1,url2};
ExtClassLoader指定java.ext.dirs下的所有文件,处理方式和AppClassLoader一样,最终这些URL将被存储在URL数组中以供sun.misc.URLclasspath使用。

  每个URLClassLoader提供相应的URL给URLclasspath中,当URLClassLoader的资源查找由URLclasspath统一处理,终究落实到URLclasspath的getResource(s)、findResource方法。资源查找方法依次在每个URL中查找资源,针对URL特殊性。
  URL以“/”结尾的,认为是一个目录:
    如果URL指定资源位于本地系统,则使用java.io.File实例如new File(url,resource),通过确定存在性返回resource的URL。
    否则使用URL实例如new URL(url,resource),通过url.connect确定存在性返回resourceURL,同时如果此URL是一个jar URL,必须使用正确的jar URL格式 jar:*****.jar!/xxxx
  否则认为是一个Jar文件,则使用专门的JarLoader加载器进行加载,这种JarLoader充分利用 ”索引“加速加载

  JAR索引
   如果一个应用程序或者 applet 捆绑到多个 JAR 文件中,那么类装载器就使用一个简单的线性搜索算法搜索类路径中的每一个元素,这使类装载器可能要下载并打开许多个 JAR 文件,直到找到所要的类或者资源。如果类装载器试图寻找一个不存在的资源,那么在应用程序或者 applet 中的所有 JAR 文件都会下载。对于大型的网络应用程序和 applet,这会导致启动缓慢、响应迟缓并浪费带宽。
从 JDK 1.3 以后,JAR 文件格式开始支持索引以优化网络应用程序中类的搜索过程,特别是 applet。JarIndex 机制收集在 applet 或者应用程序中定义的所有 JAR 文件的内容,并将这些信息存储到第一个 JAR 文件中的索引文件中。下载了第一个 JAR 文件后,applet 类装载器将使用收集的内容信息高效地装载 JAR 文件(以上“JAR索引”摘抄于http://www.ibm.com/developerworks/cn/java/j-jar/ JAR索引章节)

  JAR索引实现依赖于meta-index和index-list。 ExtClassLoader初始化时,将通过meta-index文件建立jar文件和类或资源文件之间的映射关系;按需加载类或资源的时候,
根据当前URL JAR查找这种关系,如果该JAR已经在meta-index中声明过,但文件中指定的类或资源与当前查找不匹配,则退出该JAR检索;当前JAR的JarLoader初始化时根据INDEX.LIST建立映射关系,拥有这种文件的JAR属于“主”JAR,文件中描述的其他JAR相对“主”JAR来说,是“副”JAR,当“主”JAR中查找不到对应资源的时候会依次查找"副"JAR,同时如果”副“JAR中也有INDEX.LIST文件,则根据当前文件建立和其他JAR的”主 副“关系,再找到资源之前,这种关系会根据INDEX.LIST递归建立。与当JAR中含有INDEX.LIST文件而meta-index却没指定META-INF/INDEX.LIST,这种情况下的INDEX.LIST视为无效。

  meta-index
  ExtClassLoader比AppClassLoader多做了一件事,java.ext.dirs指定的扩展目录截图

  可以清楚看到有个meta-index文件,这个文件内容如下:

% VERSION 2
% WARNING: this file is auto-generated; do not edit
% UNSUPPORTED: this file and its format may change and/or
%   may be removed in a future release
# dnsns.jar
META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
sun/net
# localedata.jar
sun/text
sun/util
# sunjce_provider.jar
com/sun/crypto/
META-INF/JCE_RSA.RSA
META-INF/JCE_RSA.SF
# sunmscapi.jar
sun/security
META-INF/JCE_RSA.RSA
META-INF/JCE_RSA.SF
# sunpkcs11.jar
sun/security
META-INF/JCE_RSA.RSA
META-INF/JCE_RSA.SF


编写一个meta-index文件注意
  •   首行必须以% VERSION 2 开始,
  •   引用的jar包相对于meta-index所在目录
  •   引用Jar的行必须以# ! @,后加一个”空格“
  •   以! 开头的jar接下来的几行指定的文件必须是class文件

这些引用的JAR包将会被添加到ExtClassloader url数组中,同时根据meta-index内容为这些JAR包建立”索引“,当加载这些JAR URL时,如果对应的“索引”存在但指定的资源不包括,则认为是“没找到”,返回NULL;

创建一个 JarIndex
可以通过在 jar命令中指定 -i选项创建一个 JarIndex。命令行中第一个JAR是”主“。


”主“JAR中新增了一个INDEX.LIST文件
JarIndex-Version: 1.0

dnsns.jar
sun
sun/net
sun/net/spi
sun/net/spi/nameservice
sun/net/spi/nameservice/dns
META-INF/services

sunjce_provider.jar
META-INF
com
com/sun
com/sun/crypto
com/sun/crypto/provider

sunpkcs11.jar
META-INF
sun/security/pkcs11
sun/security/pkcs11/wrapper

sunmscapi.jar
META-INF
sun
sun/security
sun/security/mscapi

localedata.jar
sun
sun/text
sun/text/resources
sun/util
sun/util/resources


  JarLoader加载器将充分利用Jar索引机制,如果指定的URL以”/“结尾将使用之前讨论过的Loader机制,这时Jar索引机制是无效的,所以开发中避免写URL时,不要画蛇添足多加"/"例如xxxx.jar/。同时如果找不到meta-index和index.list映射关系,那么JarLoader将jar文件的manifest.mf中指定的Class-Path URL路径添加到搜索路径中。
特殊情况:当args为空或"."时将找不到对应的条目,返回NULL


Spring中提供的资源访问API。

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}

ClassPathResource,UrlResource,FileSystemResource,ServletContextResource等都实现了。ResourceLoader接口将根据如下情况返回单个资源实例
public interface ResourceLoader {
    Resource getResource(String location);
}

public interface ResourcePatternResolver extends ResourceLoader{
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    Resource[] getResources(String locationPattern) throws IOException;
}
  Spring提供了该接口的默认实现DefaultResourceLoader:
location以“classpath:”开始,则使用ClassPathResource或其子实例
location是一个标准的URL,则使用UrlResource,不支持JAR路径
否则根据ApplicationContext(ResourceLoader子类)具体实现返回不同实例
ClassPathXmlApplicationContext 返回ClassPathResource或其子实例
FileSystemXmlApplicationContext 返回FileSystemResource
WebApplicationContext 返回ServletContextResource

  也提供了ANT风格通配符支持比如classpath*:META-INF/*-context.xml,由PathMatchingResourcePatternResolver实现:
  1.有"classpath*:”,含ANT通配符则返回所有匹配资源
  2.有"classpath*:”,不含ANT通配符则代理classloader.getResources返回多个资源
  3.无"classpath*:”,含ANT通配符则返回所有匹配资源
  4.无"classpath*:",不含ANT通配符则代理DefaultResourceLoader.getResource返回单个资源





猜你喜欢

转载自tmmh.iteye.com/blog/1865260