一、什么是classpath


  classpath,翻译过来就是类路径的意思,它是包含class文件的路径集合,用于指示虚拟机jvm在这些路径下搜索class文件。
  类路径可以同时定义多个,多个类路径之间需要使用分隔符进行分隔,windows环境下使用“;”,linux环境下则使用“:”。下面我们对类路径进行一个简单的分类,如下所示:
类路径的类型 描述
"." 表示当前目录,即执行java命令启动应用的目录。如果我们不显式的设置任何类路径,则默认它包含一个多当前目录;但如果显式设置了类路径,并且设置的路径不包含当前目录的话,则类路径就不包含当前目录了
扩展目录 (1)扩展目录可以是包含class文件的基目录,比如“/home/user/classdir”,该目录作为类路径,其下的内容就开始是包路径目录。

(2)扩展目录也可以是一些其他的文件目录,不过jvm并不能从中找到class文件,该目录用于存放一些文件以供查找使用。
jar文件 比如“/home/user/lib/Foo.jar”,该jar文件的完整路径也能作为类路径。当然,从javaSE6开始,可以通过通配符“*”一次指定多个jar,例如“/home/user/lib/*”
运行时库文件,rt.jar,jre/lib,jre/lib/ext 这些不用手动指定,它们默认包含在类路径中
 

二、如何设置classpath


  启动应用时,设置classpath非常简单:
1 #方式1
2 java -classpath 具体的类路径
3 #-classpath也可以简写成-cp
4 java -cp 具体的类路径
5 #示例
6 java -classpath .:/home/user/classdir:/home/user/lib/*
7
8 #方式2(不推荐使用),使用CLASSPATH环境变量,JVM启动时会去查找CLASSPATH这个环境变量
9 export CLASSPATH=".:/home/user/classdir:/home/user/lib/*"

  需要注意的是,某些情况下不需要我们设置classpath,比如在tomcat或者jetty中启动应用,亦或者是通过springboot可执行jar启动应用时,我们都未设置classpath,但这并不代表不用设置,而是框架或者容器替我们做好了这个事。

 

三、"classpath:"与"classpath*:"


  这是一个扩展话题,我们在spring下常常能看到使用"classpath:"或者"classpath*:"去指定一个资源文件的路径,这两个路径前缀是啥意思呢?
  其实"classpath:"或者"classpath*:"都不是java语言自带的东西,它只是spring自定义的一种路径格式前缀而已,意思是以classpath作为根目录的指定位置。举个例子,若应用启动时指定了classpath为"/home/user/classdir",那么对于以"classpath:"开头的路径"classpath:resources/myfile.xml",spring就会在"/home/user/classdir/"下去查找相对路径为myfile.xml的文件,也就是查找绝对路径为"/home/user/classdir/myfile.xml"的文件。
  需要说明的是,"classpath:/"与"classpath:"对于spring来说是没有区别的,spring框架在处理这个路径的时候,会将开头的"/"符号去掉,这点对于"classpath*:"也是一样,具体可以查看PathMatchingResourcePatternResolver的getResources方法源码。
  最后,我们需要搞清楚"classpath:"与"classpath*:"的区别,两者之间相差一个"*"号,但在查找方式上具有很大的差异,这一点网上大部分博客都是你抄我或者我抄你,要么就没说清楚,要么就直接有错误。

3.1.不包含通配符的路径

  所谓不包含通配符的路径,指的是"classpath:"以及"classpath*:"后面资源文件的路径(含文件名)不包含通配符"*、?"等,因为spring在进一步处理路径时,首先会判断路径是否包含通配符,有和没有通配符的处理方式是完全不同的,这也是我们分开讨论的原因。
  查找规则 进一步说明
classpath: (1)既可以查找类路径中扩展目录下的资源,也可以查找类路径中jar包内的资源。
(2)查找顺序是按照类路径定义的顺序逐个查找扩展目录或jar包(并不是网上说的先去查找扩展目录再查找jar包),并返回查找到的第一个资源。
(1)返回的Resource都是ClasspathResource。
(2)如果资源位于扩展目录中,从ClasspathResource中获取到的是BufferedInputStream;如果位于jar包中,获取到的则是JarURLInputStream。
classpath*: (1)既可以查找类路径中扩展目录下的资源,也可以查找类路径中jar包内的资源。
(2)返回查找到的所有的匹配文件资源,因此可以不考虑查找顺序。
(1)返回的Resource都是URLResource。
(2)如果资源位于扩展目录中,从URLResource获取到的是BufferedInputStream;如果位于jar包中,获取到的则是JarURLInputStream。
 

3.2.包含通配符的路径

  包含通配符的路径指的是"classpath:"以及"classpath*:"后面资源文件的路径(含文件名)包含通配符"*、?"等。这种情况比较复杂,对于classpath尤其如此。因为spring在查找包含通配符的路径时,首先会从路径中提取出一个不包含通配符的“根目录”,它是“从根路径开始的、一个没有通配符的最长路径”。例如,"classpath: static/image/**/icon.png"的根目录为"static/image",而"classpath: **/image/first/icon.png"的根目录为""(后面我们称为空目录),另外"classpath: i*on.png"的根目录也是""。
  可以看到,包含通配符的路径,其提取出的根目录有两种情况,一种是空目录,一种是包含了有效路径的目录,"classpath:"在这两种不同的根目录下查找行为有所区别,而"classpath*:"保持了一致,下面用一张表格归纳了它们的查找规则:
  根目录类型 查找规则 进一步说明
classpath: 空目录,即"" (1)只在类路径中的扩展目录下查找资源,无法在类路径中的jar内查找。
(2)查找过程比较复杂:假设定义了3个类路径,分别是扩展目录"dir1"和"dir2",以及通配符jar包"dir3/*",它们的顺序可以是任意的,如"dir1:dir2:dir3/*"或者"dir3/*:dir2:dir1"。step1.首先过滤掉类路径中的jar包,剩下的扩展目录保持定义顺序不变;step2.按照类路径定义的顺序逐个查找扩展目录,如果在某个扩展目录下查找到匹配的资源文件,则将查找范围锁定在该扩展目录下,并返回该扩展目录下所有匹配的资源文件。
(1)返回的Resource都是FileSystemResource,从中获取FileInputStream。
(2)实际上,之所以无法去jar包中查找,是因为ClassLoader的getResources方法在传入""时,只能返回扩展目录资源。
包含了有效路径的目录 (1)既可以查找类路径中扩展目录下的资源,也可以查找类路径中jar包内的资源。
(2)查找过程比较复杂:假设定义了3个类路径,分别是扩展目录"dir1"和"dir2",以及通配符jar包"dir3/*",它们的顺序可以是任意的,如"dir1:dir2:dir3/*"或者"dir3/*:dir2:dir1"。step1.直接按照类路径定义的顺序逐个查找扩展目录或jar包,如果查找到包含“根目录”的某个类路径,则将查找范围锁定在此类路径下,并返回此类路径下所有匹配的资源文件,如果没有匹配的就返回空。
(1)如果资源位于扩展目录中,返回的Resource是FileSystemResource,从中获取到FileInputStream。
(2)如果资源位于jar包中,返回的Resource是ClasspathResource,从中获取到JarURLInputStream。
classpath*: 空目录,即"" (1)既可以查找类路径中扩展目录下的资源,也可以查找类路径中jar包内的资源。
(2)返回查找到的所有的匹配文件资源,因此可以不考虑查找顺序。
(1)如果资源位于扩展目录中,返回的Resource是FileSystemResource,从中获取到FileInputStream。
(2)如果资源位于jar包中,返回的Resource是URLResource,从中获取到JarURLInputStream。
包含了有效路径的目录
 

3.3.总结查找规则

  现在对上面分情况讨论的查找规则进行一个总结,方便我们记忆和使用
  • "classpath*:":总是能在类路径的扩展目录和jar包中查找,并且返回所有的匹配资源。
  • "classpath:"(不含通配符):总是能按照类路径定义的顺序逐个查找扩展目录或jar包,并返回第一个匹配的资源。
  • "classpath:"(含通配符):首先判断根目录是否为"",来决定查找的类路径范围是否需要过滤掉jar包。在处理后的类路径中按照定义的顺序逐一查找,直到查找出第一个匹配的资源文件,同时锁定该资源文件所在的类路径。之后查找并返回该锁定的类路径中所有匹配的资源文件。
 

四、框架和容器中的classpath


4.1.web容器的classpath

  传统项目的部署方式是我们将应用打成war包,部署在tomcat或者jetty这样的web容器中,然后启动web容器,其classpath不需要我们手动设置,web容器在启动时会帮我们设置好,并且约定俗称的将“/path/to/WEB-INF/classes”与“/path/to/WEB-INF/lib”这两个目录设置成classpath,这也与war包的目录结构保持一致:
  • WEB-INF/classes:存放src目录java文件编译之后的class文件、xml、properties等资源配置文件。
  • WEB-INF/lib:存放依赖库。
 

4.2.springboot的classpath

  在springboot项目中,我们启动可执行jar时一般不指定classpath,那么springboot的classpath如何指定的呢?我们先来看一个典型的springboot项目打包结构:

  在META-INF中存在清单文件MANIFEST.MF,打开该文件查看里面的内容,我们找到main-class属性指定的并非是我们编写的应用启动类ServiceFeignApplication,而是org.springframework.boot.loader.JarLauncher。

  在可执行jar包对应的目录中找到这个类,进行反编译。通过反编译代码我们大概可以推断出JarLauncher这个类添加了两个新的classpath,即“path/to/BOOT-INF/classes”与“/path/to/BOOT-INF/lib”,这两个目录类似于传统war包中的“WEB-INF/classes”与“WEB-INF/lib”。
  我们的研究当然不止于此,继续深入的看一下springboot中的另外一个类“WebMVCAutoConfiguration”,其中的方法addResourcesHandlers调用了ResourcesProperties类的getLocations方法,在该方法中,我们发现了springboot定义了4个查找静态资源或者配置文件的默认路径,它们分别是:
  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/
  如果将classpath代入,便可知静态资源实际在jar中的查找位置了,这与springboot将静态资源“resources/static”目录的打包位置一致,下图为jar包中“BOOT-INF/classes”目录下的内容:

4.3.自定义打包的classpath

  之前做过一个项目,使用maven-assembly插件对springboot web项目进行定制打zip包,由于客户规定了打包目录结构,因此无法使用springboot默认的打包结构,包结构如下:
  为了使得springboot能够正确查找到静态页面文件,选择的解决方式是,在启动脚本文件中,将“/path/to/WEB-INF”添加到classpath中,这样就满足了“classpath:/static/”的查找路径,同时为了保证conf与lib目录也能被查找,也将“/path/to/WEB-INF/conf”与“/path/to/WEB-INF/lib”添加到classpath中。    

理解classpath的更多相关文章

  1. JAVA classpath jar问题[zz]

    classpath问题可以说是所有初学者头疼的问题,偶也一样. 1) classpath的作用:  它的作用就事让java找到你所要执行,或你拥有的类. 2) classpath的设置:  设置cla ...

  2. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  3. java与java学习路线

    JAVA学习路线图 Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征.Java语言作为静态面 ...

  4. 20155321 2016-2017-2 《Java程序设计》第一周学习总结

    学习目标 [√]了解Java基础知识 [√]了解JVM.JRE与JDK,并下载.安装.测试JDK [√]了解PATH.CLASSPATH.SOURCEPATH的作用并会设置 [√]初步使用IDE(推荐 ...

  5. 20155337祁家伟 2016-2017-2 《Java程序设计》第2周学习总结

    20155337 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 这周我学习了从JDK到IDE的学习内容,简单来说分为以下几个部分 使用命令行和IDE两种方式 ...

  6. 手动编译java的package问题,及演示继承的基本实现

    不用IDE,而直接用命令编译JAVA包,仔细看了下,作一个记录. 以下的URL值得收藏. http://www.aiuxian.com/article/p-2115485.html http://ww ...

  7. 一文彻底搞懂Java中的环境变量

    一文搞懂Java环境变量 记得刚接触Java,第一件事就是配环境变量,作为一个初学者,只知道环境变量怎样配,在加上各种IDE使我们能方便的开发,而忽略了其本质的东西,只知其然不知其所以然,随着不断的深 ...

  8. 理解根目录,classpath, getClass().getResourceAsStream和getClass().getClassLoader().getResourceAsStream的区别

    一: 理解根目录 <value>classpath*:/application.properties</value> <value>classpath:/appli ...

  9. path、classpath理解

    path.classpath最常见的场景:环境变量配置 path环境变量:设置path的作用是让操作系统可以找到JDK命令(指定了JDK命令搜索路径):path环境变量原来Windows里面就有,只需 ...

随机推荐

  1. 微信小程序全局数据globalData的使用问题

    如果在A页面设置全局属性,但在B页面无法使用的话,可能是这个问题: app.js globalData: { helpPage:0, }, A页面 A(e) { getApp().globalData ...

  2. Mysql生成UUID的SQL语句

    大写的UUID: SELECT UPPER(UUID()); 小写的UUID: SELECT LOWER(UUID()); SELECT UUID(); 去掉横杠的UUID: SELECT REPLA ...

  3. js清空input file的值

    原文:js清空input file的值 在做选择本地图片上传的功能时遇到一个问题,第一次点file按钮选择图片完成会触发onchange事件,获取文件后动态在界面上创建img标签展示,但把创建的img ...

  4. 【js】--获取开始时间 和 截止时间中间的所有时间

    1.工具函数  将[中国标准时间] 转换成 [年月日 时分秒] /* * timeStamp: 标准时间 例: 'Tue Sep 22 2020 00:00:00 GMT+0800 (中国标准时间)' ...

  5. Java中的比较器(排序)

    "顺序"在任何一个领域里都是非常重要的一个概念,程序也不例外.不同的执行顺序,能对你的执行结果产生直接影响. 既然涉及到顺序,那就要求排序.所以本文讨论的就是排序中使用到的比较器C ...

  6. new和delete关键字

    new关键字创建出来的对象位于什么地方?很明显嘛,new关键字创建出来的对象一定位于堆空间,这种说法一定正确吗?本篇博客帮你揭开其神秘的面纱. 被忽略的事实new/delete的本质是C++预定义的操 ...

  7. 关于int和Integer缓存(二):修改缓存大小

    续上文: java中的基础数据类型长度是否取决于操作系统? 在一些语言中,数据类型的长度是和操作系统有关系的,比如c和c++: 但是在java中,java的基础类型长度都是固定的,都是4个字节.因为j ...

  8. ProjectEuler 006题

    题目: The sum of the squares of the first ten natural numbers is, 12 + 22 + ... + 102 = 385 The square ...

  9. 4种Golang并发操作中常见的死锁情形

    摘要:什么是死锁,在Go的协程里面死锁通常就是永久阻塞了,你拿着我的东西,要我先给你然后再给我,我拿着你的东西又让你先给我,不然就不给你.我俩都这么想,这事就解决不了了. 本文分享自华为云社区< ...

  10. Java实现小程序微信支付

    小程序支付流程交互图: 进入小程序,下单,请求下单支付,调用小程序登录API来获取Openid,生成商户订单 // pages/pay/pay.js var app = getApp(); Page( ...