java资源路径

  在获取配置文件内容的时候,突然发现针对同一个资源文件,getResource(FileName)和FileInputStream(FilePath)两个要输入的参数不同。即两种方式能获取文件的目录范围不同,同时在不同类型的项目中需要对应填入需要的相对路径或绝对路径。

  以下测试主要在JavaProject中进行(JavaProject是最普通的运行在本计算机,与本机用户交互,完成一定功能的java应用程序)

  源文件目录:

 

  1.通过FileInputStream(FilePath)获取资源

      

InputStream in1 = new BufferedInputStream(new FileInputStream("conf/jdbc.properties"));


    Java Project上述方法在IDE(eclipse)中运行时不会有问题,因为eclipse会自动到项目的根目录下寻找,但是如果将项目打包放到其他地方运行时就会发现报资源找不到错误。解决方法是将文件目录写成绝对路径,但是此种方法不利于程序移植。
 
  2.通过Class对象获取资源
    此方法多用在需要打包到其他运行环境中运行的程序,因为此种方法所依赖的路径是装载类的路径,与实际的项目工程所在的路径联系不大(推荐这种方法,因为打包的文件来源都是classpath里的文件)。
    所谓类装载器(ClassLoader)就是负责从Java字符文件流读入内存,并构造Class类对象。而Java字符文件则是从源文件编译后存放的字节码(.class)文件的目录中获取(此目录在.classpath中的output可改变)。所以可以看出,如果使用的是Class类getClass()获取路径,那么程序能检索的路径是项目编译文件存放的目录之内。
    ClassLoader在Java中通过getClassLoader()来获得。查看getClassLoader(),可以看到java对类加载器的说明:
    载类的过程非常简单:查找类所在位置,并将找到的Java类的字节码装入内存,生成对应的Class对象。Java的类装载器专门用来实现这样的过程,JVM并不止有一个类装载器,事实上,如果你愿意的话,你可以让JVM拥有无数个类装载器,当然这除了测试JVM外,我想不出还有其他的用途。你应该已经发现到了这样一个问题,类装载器自身也是一个类,它也需要被装载到内存中来,那么这些类装载器由谁来装载呢,总得有个根吧?没错,确实存在这样的根,它就是神龙见首不见尾的Bootstrap ClassLoader. 为什么说它神龙见首不见尾呢,因为你根本无法在Java代码中抓住哪怕是它的一点点的尾巴,尽管你能时时刻刻体会到它的存在,因为java的运行环境所需要的所有类库,都由它来装载,而它本身是C++写的程序,可以独立运行,可以说是JVM的运行起点。在Bootstrap完成它的任务后,会生成一个AppClassLoader(实际上之前系统还会使用扩展类装载器ExtClassLoader,它用于装载Java运行环境扩展包中的类),这个类装载器才是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,我们假定程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类通通会由它来装载。AppClassLoader查找类的区域就是耳熟能详的Classpath,也是初学者必须跨过的门槛,有没有灵光一闪的感觉,我们按照它的类查找范围给它取名为类路径类装载器。还是先前假定的情况,当Java中出现新的类,AppClassLoader首先在类传递给它的父类类装载器,也就是Extion ClassLoader,询问它是否能够装载该类,如果能,那AppClassLoader就不干这活了,同样Extion ClassLoader在装载时,也会先问问它的父类装载器。我们可以看出类装载器实际上是一个树状的结构图,每个类装载器有自己的父亲,类装载器在装载类时,总是先让自己的父类装载器装载,如果父类装载器无法装载该类时,自己就会动手装载,如果它也装载不了,那么对不起,它会大喊一声:Exception,class not found。有必要提一句,当由直接使用类路径装载器装载类失败抛出的是NoClassDefFoundException异常。如果使用自定义的类装载器loadClass方法或者ClassLoader的findSystemClass方法装载类,如果你不去刻意改变,那么抛出的是ClassNotFoundException。
    这里jdk告诉我们:如果一个类是通过bootstrap 载入的,那我们通过这个类去获得classloader的话,有些jdk的实现是会返回一个null的,比如说我用 new Object().getClass().getClassLoader()的话,会返回一个null,这样的话上面的代码就会出现NullPointer异常.所以保险起见我们最好还是使用我们自己写的类来获取classloader("this.getClass().getClassLoader()“),这样一来就不会有问题。
    Java设计Class用于JVM对类的管理。JVM通过Class来识别类.当一个类被虚拟机装载完毕的时候,就会创建一个Class类的实例,对于类A就是A.class,对于类B就是B.class. Class类也提供了许多方法来获取类的信息. 要知道,类的装载器分为 "启动类装载器 ", "用户定义装载器 ".它不止一种 Class类需要保存这些信息. getClassLoader()是用来获取这个信息的。
  
  获取资源文件的方法说明:
  getClass():取得当前对象所属的Class对象
  getResourceAsStream () 返回的是inputstream
  getResource() 返回:URL
  Class.getResource("") 返回的是当前Class这个类所在包开始的为置
  Class.getResource("/") 返回的是classpath的位置
  getClassLoader().getResource("") 返回的是classpath的位置
  getClassLoader().getResource("/") 错误的!!
  上述getResource("")与getClassLoader().getResource("")的区别在与getClassLoader()得到的路径是classpath的根目录,因为类加载器都是从根目录开始检索。基本上,两个都可以用于从 classpath 里面进行资源读取, classpath包含classpath中的路径和classpath中的jar。
  两个方法的区别是资源的定义不同, 前者主要用于相对与一个object取资源,而后者用于取相对于classpath的资源,用的是绝对路径。
前者资源路径有两种方式, 一种以 / 开头,则这样的路径是指定绝对路径, 如果不以 / 开头, 则路径是相对与这个class所在的包的。而后者路径直接使用相对于classpath的绝对路径。
  Classpath目录:
 
 
  代码:
 
  System.out.println("user.dir:"+System.getProperty("user.dir"));
  System.out.println("Class.getClass():"+DBconnect.class.getClass());
  System.out.println("Class.getClassLoader():"+DBconnect.class.getClassLoader());
  System.out.println("Class.getResource(''):"+DBconnect.class.getResource(""));
  System.out.println("Class.getResource('/'):"+DBconnect.class.getResource("/")); 
  System.out.println("Class.Resource('jdbc.properties'):"+DBconnect.class.getResource("jdbc1.properties"));
  System.out.println("Class.getClassLoader().getClass():"+DBconnect.class.getClassLoader().getClass());
  System.out.println("Class.getClassLoader().getResource(''):"+DBconnect.class.getClassLoader().getResource(""));
  System.out.println("Class.getClassLoader().getResource('jdbc.properties')"+DBconnect.class.getClassLoader().getResource("jdbc.properties"));
  System.out.println("Class.getClassLoader().getResource('jdbc1.properties')"+DBconnect.class.getClassLoader().getResource("com/yunyi/exportdblog/jdbc1.properties"));

//其他获取classpath的方法--new File()得到的路径不带file:
System.out.println(Thread.currentThread().getContextClassLoader().getResource("")); 
System.out.println(ClassLoader.getSystemResource(""));
System.out.println(new File("/").getAbsolutePath());
  结果: 
  user.dir:D:\workspace3\DBLogExport
  Class.getClass():class java.lang.Class
  Class.getClassLoader():sun.misc.Launcher$AppClassLoader@73d16e93
  Class.getResource(''):file:/D:/workspace3/DBLogExport/bin/com/yunyi/exportdblog/
  Class.getResource('/'):file:/D:/workspace3/DBLogExport/bin/
  Class.Resource('jdbc.properties'):file:/D:/workspace3/DBLogExport/bin/com/yunyi/exportdblog/jdbc1.properties
  Class.getClassLoader().getClass():class sun.misc.Launcher$AppClassLoader
  Class.getClassLoader().getResource(''):file:/D:/workspace3/DBLogExport/bin/
  Class.getClassLoader().getResource('jdbc.properties')file:/D:/workspace3/DBLogExport/bin/jdbc.properties
  Class.getClassLoader().getResource('jdbc1.properties')file:/D:/workspace3/DBLogExport/bin/com/yunyi/exportdblog/jdbc1.properties

  注1.在Java Project中,classpath只会将src中的源文件编译到classpath文件夹中,其余的用户自建的文件夹则不会被编译进去,所以有两种解决方法:
      a.将所需要的文件放到src中
      b.在.classpath中将创建的文件加入到编译目录中(若是classpath文件中的文件被手动删除了,需要重新build项目才能加需要的文件加入到classpath中):    
    <classpathentry kind="src" path="conf"/>
  注2. 在静态方法中,如果使用getClass()会报错。因为getClass省略了this,而this是不能在static(静态)方法或者static块中使用的,原因是static类型的方法或者代码块是属于类本身的,不属于某个对象,而this本身就代表当前对象,而静态方法或者块调用的时候是不用初始化对象的。
      问题是:假如我不想让某个类有对象,那么我会将此类的默认构造方法设为私有,当然也不会写别的共有的构造方法。并且我这个类是工具类,都是静态的方法和变量,我要在静态块或者静态方法中获取properties文件,这个方法就行不通了。
那怎么办呢?其实这个类就不是这么用的,他仅仅是需要获取一个Class对象就可以了,那还不容易啊--取所有类的父类Object,用Object.class难道不比你的用你正在写类自身方便安全吗 ?(注:以上摘自熔岩)当然,把Object.class换成int.class照样行。
     另外,如果是static方法或块中读取Properties文件,还有一种最保险的方法,就是这个类的本身名字来直接获取Class对象,比如本例中可写成TestProperties.class,这样做是最保险的方法,如例子。

补充:文件路径获取方法
  1.相对路径的获得
    说明:相对路径(即不写明时候到底相对谁)均可通过以下方式获得(不论是一般的java项目还是web项目)  
  String relativelyPath=System.getProperty("user.dir"); 
   上述相对路径中,java项目中的文件是相对于项目的根目录 ,结果见上例。
    web项目中的文件路径视不同的web服务器不同而不同(tomcat是相对于 tomcat安装目录\bin)
  2.类加载目录的获得(即当运行时某一类时获得其装载目录)
    a.不论是一般的java项目还是web项目,先定位到能看到包路径的第一级目录
   InputStream is=PropertiesLoad.class.getClassLoader().getResourceAsStream("jdbc.properties");
       b.此方法和1中的方法类似,不同的是此方法必须以'/'开头  
   InputStream is=PropertiesLoad.class.getResourceAsStream("/jdbc.properties");
      上述路径查询同getResource();
  3.web项目根目录的获得(发布之后) 
    上面的2中在非WEB应用的环境中,只需要简单的通过类加载器的getResourceAsStream方法读取文件。
    Jsp下的application内置对象就是上面的ServletContext的一种实现。 读取资源文件一般根据上下文环境分为两种情况。
    WEB应用的环境中,因为应用包含在了Servlet 容器中,所以情况相对来说要复杂一些。同上一样,读取classpath中的资源,依然通过类加载读取,但是通过上下文的类加载器中去读。
    a.从Servlet出发
      可建立一个servlet在其的init方法中写入如下语句
  ServletContext contexts=this.getServletContext(); 
  String temp =context1.getRealPath("/");//关键
  String temp1 = context1.getRealPath("");
     结果:
     temp:D:\workspace3\Tomcat-test\webapps\webapplication\
     temp1:D:\workspace3\Tomcat-test\webapps\webapplication
     webapplication为项目名字.
     ServletContext. getResourceAsStream(String path):默认从WebAPP根目录下取资源,Tomcat下path是否以’/'开头无所谓,当然这和具体的容器实现有关。
    b.从httpServletRequest出发    
   String requestPath=request.getSession().getServletContext().getRealPath("/");
     结果:D:\workspace3\Tomcat-test\webapps\webapplication\
  4.classpath的获取(在Eclipse中为获得src或者classes目录的路径)
    a.通过Thread
   String t=Thread.currentThread().getContextClassLoader().getResource("").getPath();
   System.out.println("t---"+t);
      结果:t---/E:/order/002_ext/WebRoot/WEB-INF/classes/
    b.使用JdomParse,JdomParse为src某一个包中的类
<span style="font-family:Arial;background-color: transparent;">      </span>String p1=JdomParse.class.getClassLoader().getResource("").getPath();
   System.out.println("JdomParse.class.getClassLoader().getResource--"+p1);
     结果:JdomParse.class.getClassLoader().getResource--/E:/order/002_ext/WebRoot/WEB-INF/classes/
     另外,如果想把文件放在某一包中,则可以 通过以下方式获得到文件(先定位到该包的最后一级目录)    
  String p2=JdomParse.class.getResource("").getPath(); 
  System.out.println("JdomParse.class.getResource---"+p2);
    结果:JdomParse.class.getResource---/E:/order/002_ext/WebRoot/WEB-INF/classes/jdom/ 
  5.属性文件的读取-附加    
   Locale locale = Locale.getDefault();  
   ResourceBundle localResource = ResourceBundle.getBundle("test/propertiesTest", locale);
   String value = localResource.getString("test"); 
   System.out.println("ResourceBundle: " + value);
     工程src目录下propertiesTest.properties(名字后缀必须为properties)文件内容如下:  test=hello word
  6.不通过Servlet
     首先写一个接听类 (推荐使用,容器启动时就执行,不会抛空指针异常,适合做定时器任务来删除服务器文件的路径) 
  public class PathListener implementsServletContextListener {
      private staticServletContext servletContext;
      public voidcontextDestroyed(ServletContextEvent sce) {
         this.servletContext= sce.getServletContext();
         System.out.println("path=======:"+servletContext.getRealPath("/"));
      }
      public voidcontextInitialized(ServletContextEvent arg0) {
      }
  }
     在web.xml中加入如下配置:
  <listener>
       <listener-class>com.chinacreator.report.listener.PathListener</listener-class>
  </listener> 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值