转:Java 资源加载

处理配置文件对于Java程序员来说再常见不过了,不管是Servlet,Spring,抑或是Structs,都需要与配置文件打交道。Java将配置文件当作一种资源(resource)来处理,并且提供了两个类来读取这些资源,一个是Class类,另一个是ClassLoader类。

 

当我们自己的程序需要处理配置文件时(比如xml文件或properties文件),通常会遇到两个问题:

  (1)我的配置文件应该放在哪里?

  (2)怎么我的配置文件找不到了?

 

在了解了Java加载资源文件的机制后,以上这两个问题便迎刃而解了。

对于第一个问题,答案是:请将你的资源文件放在classpath里,如果资源文件在jar中,请将该jar文件也加到classpath里面。

对于第二个问题,就得看你是使用的是哪个类(Class还是ClassLoader)来加载资源文件了,所以接下来分别讨论一下Class类和ClassLoader类对于资源文件的加载机制。

 

(一)用Class类加载资源文件

通过调用Class类的getResourceAsStream方法来加载资源文件:

public InputStream getResourceAsStream(String pathToConfigFile);

 

该方法接收一个String类型的参数(pathToConfigFile)来表示资源文件的地址,如果加载成功,则返回该资源文件的输入流(InputStream),如果失败,则返回null。重要的是,在传入pathToConfigFile参数时,有两种方式,第一种方式为绝对定位方式,即pathToConfigFile以"/"开头,此时Java以classpath为根目录,直接加上pathToConfigFile来搜索资源文件。第二种方式为相对定位方式,即pathToConfigFile不以"/"开头,此时资源文件的全路径应该为:调用getResourceAsStream方法的类的package路径加上pathToConfigFile。(在将package转为目录时将"."变成"/") 

举个例子,在IntelliJ Idea中创建一个java工程,目录结构如下:

该工程里有两个resources文件夹,一个位于davenkin文件夹下,一个直接位于src文件夹下。第一个resources文件夹下有一个config.properties文件,其内容为:

name = ConfigUnderDavenkin

第二个resources文件夹下也有一个config.properties文件,其内容为:

name = ConfigUnderSrc

在davenkin包下定义ResourceLoader.java来加载资源文件:

package davenkin;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ResourceLoader
{
    public static void main(String[] args) throws IOException
    {
        ResourceLoader resourceLoader = new ResourceLoader();
        resourceLoader.loadProperties1();

    }

    public void loadProperties1() throws IOException
    {
        InputStream input = null;
        try
        {
            input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties");

            //also can be this way:
            //input = this.getClass().getResourceAsStream("/resources/config.properties");
        } catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        printProperties(input);
    }

    private void printProperties(InputStream input) throws IOException
    {
        Properties properties = new Properties();
        properties.load(input);
        System.out.println(properties.getProperty("name"));
    }
}

输出结果为第二个resources文件夹下config.properties的内容:

ConfigUnderSrc

原因在于(请注意ReourceLoader.java文件中的红色部分):我们给出的资源文件路径(/resources/config.properties)以"/"开头,即使用的是绝对定位方式,所以找到的是直接在classpath下的resources文件夹。如果去掉资源文件文件路径前的"/",则采用的是相对定位方式,此时应该输出davenkin/resources/config.properties文件的内容。

(二)用ClassLoader类加载资源文件

ClassLoader类也提供和Class类相同的加载方法:

public InputStream getResourceAsStream(String pathToConfigFile);

用ClassLoader加载配置文件时,pathToConfigFile均不能以"/"开头,在查找时直接在classpath下进行查找。Class类在查找资源文件时,也是代理(delegate)给ClassLoader完成查找功能的,请参考Java官方文档

在使用Class和ClassLoader加载资源文件时,有几种区别细微的方法,修改ResourceLoader.java文件如下:

package davenkin;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ResourceLoader
{
    public static void main(String[] args) throws IOException
    {
        ResourceLoader resourceLoader = new ResourceLoader();
        resourceLoader.loadProperties1();
        resourceLoader.loadProperties2();
        resourceLoader.loadProperties3();
        resourceLoader.loadProperties4();
        resourceLoader.loadProperties5();
        resourceLoader.loadProperties6();
    }

    public void loadProperties1() throws IOException
    {
        InputStream input = null;
        try
        {
            input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties");
        } catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        printProperties(input);
    }

    public void loadProperties2() throws IOException
    {
        InputStream input = null;

        input = this.getClass().getResourceAsStream("/resources/config.properties");
        printProperties(input);
    }

    public void loadProperties3() throws IOException
    {
        InputStream input = this.getClass().getResourceAsStream("resources/config.properties");
        printProperties(input);
    }

    public void loadProperties4() throws IOException
    {
        InputStream input = this.getClass().getClassLoader().getResourceAsStream("resources/config.properties");
        printProperties(input);
    }

    public void loadProperties5() throws IOException
    {
        InputStream input = ClassLoader.getSystemResourceAsStream("resources/config.properties");
        printProperties(input);
    }

    public void loadProperties6() throws IOException
    {
        InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("resources/config.properties");

        printProperties(input);
    }

    private void printProperties(InputStream input) throws IOException
    {
        Properties properties = new Properties();
        properties.load(input);
        System.out.println(properties.getProperty("name"));
    }
}

以上程序输出结果为(请仔细揣摩,稍不小心(比如多加了一个"/"或少加了一个"/"),就会报NullPointerException异常,表明你的资源文件没有找到):

ConfigUnderSrc
ConfigUnderSrc
ConfigUnderDavenkin
ConfigUnderSrc
ConfigUnderSrc
ConfigUnderSrc
来自:http://www.cnblogs.com/CloudTeng/archive/2012/04/08/2438028.html

另一篇:
mport java.net.URL; 
 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
public class TestMain { 
    public static void main(String[] args) { 
 
        // ############################################################################################################ 
        // 1:使用本类的Class类的getResource()方法 
        // 在当前包寻找资源(指定相对路径,其他均返回null。) 
        URL filePathUrl1 = TestMain.class.getResource("beans_sameLocation.xml"); 
 
        // 在根寻找资源(需要文件分隔符"/",其他均返回null。) 
        URL filePathUrl2 = TestMain.class.getResource("/beans.xml"); 
 
        // 在不同包内寻找资源(指定相对路径(需要文件分隔符"/"),其他均返回null。) 
        URL filePathUrl3 = TestMain.class.getResource("/test/spring/beanpost/file/beans_diffLocation.xml"); 
 
        // ############################################################################################################ 
        // 2:使用本类的Class类的ClassLoader类的getResource()方法 
        // 在相同包内寻找资源,总是返回null。 
        // URL filePathUrl3 = 
        // TestMain.class.getClassLoader().getResource("beans_sameLocation.xml"); 
 
        // 在根寻找资源,指定相对路径,其他均返回null。 
        URL filePathUrl4 = TestMain.class.getClassLoader().getResource("beans.xml"); 
 
        // 在不同包内寻找资源,指定相对路径,其他均返回null。 
        URL filePathUrl5 = TestMain.class.getClassLoader().getResource("test/spring/beanpost/file/beans_diffLocation.xml"); 
 
        // ############################################################################################################ 
        // 3:使用ClassLoader类的getSystemResource()方法 
        // 在指定包内寻找资源,指定相对路径,其他均返回null。 
        URL filePathUrl6 = ClassLoader.getSystemResource("test/spring/beanpost/beans_sameLocation.xml"); 
        // 同上 
        URL filePathUrl7 = ClassLoader.getSystemClassLoader().getResource("test/spring/beanpost/beans_sameLocation.xml"); 
 
        // 在根寻找,指定相对路径,其他均返回null。 
        URL filePathUrl8 = ClassLoader.getSystemResource("beans.xml"); 
        // 同上 
        URL filePathUrl9 = ClassLoader.getSystemClassLoader().getResource("beans.xml"); 
 
        // ############################################################################################################ 
        // 4:使用Thread加载资源(推荐此方法) 
        // 在指定包内寻找资源,(相对路径),其他均返回null。 
        filePathUrl6 = Thread.currentThread().getContextClassLoader().getResource("test/spring/beanpost/beans_sameLocation.xml"); 
 
        // 在根寻找,(相对路径),其他均返回null。 
        filePathUrl7 = Thread.currentThread().getContextClassLoader().getResource("beans.xml"); 
 
        // 在不同包内寻找资源,(相对路径),其他均返回null。 
        filePathUrl8 = Thread.currentThread().getContextClassLoader().getResource("test/spring/beanpost/file/beans_diffLocation.xml"); 
 
        // ############################################################################################################ 
 
        System.out.println(filePathUrl1.getFile()); 
        System.out.println(filePathUrl2.getFile()); 
        System.out.println(filePathUrl3.getFile()); 
        System.out.println(filePathUrl4.getFile()); 
        System.out.println(filePathUrl5.getFile()); 
        System.out.println(filePathUrl6.getFile()); 
        System.out.println(filePathUrl7.getFile()); 
        System.out.println(filePathUrl8.getFile()); 
        System.out.println(filePathUrl9.getFile()); 
        System.out.println("----------------------------------------------------------------------------------------"); 
        System.getProperties().list(System.out); 
        System.out.println("----------------------------------------------------------------------------------------"); 
 
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); 
        Animal animal = (Animal) ac.getBean("animal"); 
        System.out.println(animal.speak()); 
        animal.setAge(88); 
 
        Animal animal0 = (Animal) ac.getBean("animal"); 
        System.out.println(animal0.speak()); 
 
        ApplicationContext ac1 = new ClassPathXmlApplicationContext("beans.xml"); 
        Animal animal1 = (Animal) ac1.getBean("animal"); 
        System.out.println(animal1.speak()); 
    } 
} 

可以参考:http://www.haogongju.net/art/1680442 写的很好。

 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file = new File("c:/test.txt");这样用有什么问题,相信大家都知道,就是路径硬编码,对于JAVA精神来说,应用应该一次成型,到处可用,并且从现实应用来讲,最终生成的应用也会部署到Windows外的操作系统中,对于linux来说,在应用中用了c:/这样的字样,就是失败,所以,我们应该尽量避免使用硬编码,即直接使用绝对路径。

  在Servlet应用中,有一个getRealPath(String str)的方法,这个方法尽管也可以动态地获得文件的路径,不秘直接手写绝对路径,但这也是一个不被建议使用的方法,那么,我们有什么方法可以更好地获得文件呢?

      那就是Class.getResource()与Class.getResourceAsStream()方法,但很多人还是不太懂它的用法,因为很多人(比如不久前的我)都不知道应该传怎么样的参数给它,当然,有些人己经用得如火纯青,这些人是不需要照顾的,在此仅给不会或者还不是很熟的人解释一点点。

比如我们有以下目录

|--project

    |--src

        |--javaapplication

            |--Test.java

            |--file1.txt

        |--file2.txt

    |--build 

        |--javaapplication

            |--Test.class

            |--file3.txt

        |--file4.txt

在上面的目录中,有一个src目录,这是JAVA源文件的目录,有一个build目录,这是JAVA编译后文件(.class文件等)的存放目录

那么,我们在Test类中应该如何分别获得

file1.txt  file2.txt  file3.txt  file4.txt这四个文件呢?

首先讲file3.txt与file4.txt

file3.txt:

方法一:File file3 = new File(Test.class.getResource("file3.txt").getFile());  url.getFile Gets the file name of this URL

方法二:File file3 = new File(Test.class.getResource("/javaapplication/file3.txt").getFile());

方法三:File file3 = new File(Test.class.getClassLoader().getResource("javaapplication/file3.txt").getFile());

file4.txt:

方法一:File file4 = new File(Test.class.getResource("/file4.txt").getFile());

方法二:File file4 = new File(Test.class.getClassLoader().getResource("file4.txt").getFile());

很好,我们可以有多种方法选择,但是file1与file2文件呢?如何获得?

答案是,你只能写上它们的绝对路径,不能像file3与file4一样用class.getResource()这种方法获得,它们的获取方法如下

假如整个project目录放在c:/下,那么file1与file2的获取方法分别为

file1.txt

方法一:File file1 = new File("c:/project/src/javaapplication/file1.txt");

方法二:。。。没有

file2.txt

方法一:File file2 = new File("c:/project/src/file2.txt");

方法二:。。。也没有

总结一下,就是你想获得文件,你得从最终生成的.class文件为着手点,不要以.java文件的路径为出发点,因为真正使用的就是.class,不会拿个.java文件就使用,因为java是编译型语言嘛

至于getResouce()方法的参数,你以class为出发点,再结合相对路径的概念,就可以准确地定位资源文件了,至于它的根目录嘛,你用不同的IDE build出来是不同的位置下的,不过都是以顶层package作为根目录,比如在Web应用中,有一个WEB-INF的目录,WEB-INF目录里面除了web.xml文件外,还有一个classes目录,没错了,它就是你这个WEB应用的package的顶层目录,也是所有.class的根目录“/”,假如clasaes目录下面有一个file.txt文件,它的相对路径就是"/file.txt",如果相对路径不是以"/"开头,那么它就是相对于.class的路径。。

还有一个getResourceAsStream()方法,参数是与getResouce()方法是一样的,它相当于你用getResource()取得File文件后,再new InputStream(file)一样的结果

via:http://gavin-chen.iteye.com/blog/261151

难经2:URL.getFile(),是你让老虎落入陷阱?:http://liuu.iteye.com/blog/300618

url使用例子:

// File Name : URLDemo.java

import java.net.*;
import java.io.*;

public class URLDemo
{
   public static void main(String [] args)
   {
      try
      {
         URL url = new URL(args[0]);
         System.out.println("URL is " + url.toString());
         System.out.println("protocol is "
                                    + url.getProtocol());
         System.out.println("authority is "
                                    + url.getAuthority());
         System.out.println("file name is " + url.getFile());
         System.out.println("host is " + url.getHost());
         System.out.println("path is " + url.getPath());
         System.out.println("port is " + url.getPort());
         System.out.println("default port is "
                                   + url.getDefaultPort());
         System.out.println("query is " + url.getQuery());
         System.out.println("ref is " + url.getRef());
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}

A sample run of the thid program would produce following result:

$ java URLDemo http://www.amrood.com/index.htm?language=en#j2se
URL is http://www.amrood.com/index.htm?language=en#j2se
protocol is http
authority is www.amrood.com
file name is /index.htm?language=en
host is www.amrood.com
path is /index.htm
port is -1
default port is 80
query is language=en
ref is j2se

注意file name 包括路径。

 

原文地址:https://www.cnblogs.com/youxin/p/2783677.html