zoukankan      html  css  js  c++  java
  • 理解classpath

    一、什么是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中。    
  • 相关阅读:
    SpringBoot启动配置原理
    SpringBoot和web开发
    SpringBoot和日志
    SpringBoot入门
    Docker仓库
    Docker Swarm
    Docker相关概念
    Docker服务编排
    Dockerfile
    Docker应用部署
  • 原文地址:https://www.cnblogs.com/manayi/p/15340455.html
Copyright © 2011-2022 走看看