路径classpath,classpath*,以及file:
./ 当前目录
../上一层目录
/是根目录
1. classpath : 类路径,指的是编译后的字节码文件存储路径,一般为target目录下的classes目录(java项目),在web项目中指的是WEB-INF下的classes目录。实际上,两者其实是一致的,web项目的classes目录也是工程编译后的产物。
举个栗子 classpath:applicationContext.xml 表示仅会去当前工程的类路径下寻找applicationContext.xml文件,而且只会在classes这一次层找,如果配置在下一层,比如classes/spring文件夹下,是不会进行搜索的,这时候就该这么配置classpath:spring/applicationContext.xml。
2. classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。而且不仅限于classes当前目录下,也会对其子目录进行搜索。(已验证正确性)
classpath*的使用是为了多个component(最终发布成不同的jar包)并行开发,各自的bean定义文件按照一定规则:package+filename,而使用这些component的调用者可以把这些文件都加载进来。classpath*的加载使用了classloader的 getResources() 方法,用classpath*需要遍历所有的classpath,所以加载速度是很慢的。
3. file: 作为URL从文件系统中加载,这种方式通常配置相对路径,相对于当前路径。另外file:///通常表示的是本地文件的绝对路径。这里要注意一下,从文件系统中加载,意味着路径结构对应的是电脑的实际目录,jar包内部的目录结构是不使用的,它将被视为一个整体的文件。
举个例子:图示jar包为一个可执行jar包,在选择配置时,如果想选择图1中独立出来的conf文件下的配置,需要使用
<import resource="file:./conf/nacos.xml"/>
显然这里的相对路径相对的基点不再是app.xml所在的项目中的路径,而是整个jar包相对于电脑的文件系统路径,它和外部conf文件是同一层级的。
如果想使用图2项目中设置的配置nacos.xml,就可以配置路径方式为
<import resource="nacos.xml"/>
这里的相对路径很明显就是相对于当前工程了。
附:在打包某个工程时,要求将依赖一起合并打成一个可执行jar包,这时候就需要将依赖加入到classpath中(具体做法参考maven部分笔记),此时classpath 和classpath* 就没有什么区别了~~
最近研究dubbo这个框架遇到一个问题,dubbo的生产者都是通过jar来启动的,结合公司的项目都是通过war启动的,war包启动可以在war解压以后运维人员更改配置文件(原则上生产环境的配置只有运维人员才能看到)。但是jar包不可能让运维人员解压更改配置才从新打jar包。所以jar包启动的项目需要能够让运维人员方便快捷的更改配置。解决方案是将配置文件放在jar包外面。spring加载外部配置文件。
前缀 | 说明 |
---|---|
classpath | 优先本项目class路径查找,没有的话依次在其他jar包中class路径查找,找到第一个匹配的为止 |
classpath* | 加载到所有class路径下的配置文件,包括jar包的class路径下,最终加载所有匹配的 |
file | 通过URL加载,来源为文件系统(注意文件读取权限) |
http | 通过URL加载,来源为网络 |
(none) | 根据 ApplicationContext 进行判断(这个我没试过) |
classpath
<context:property-placeholder location="classpath:system.properties" />
举个栗子:A项目中resources中存在system.properties,B项目中resources中也存在system.properties。A项目以jar包形式引入B项目。此时A项目spring配置classpath:system.properties加载到的是A项目自己的system.properties。但是如果A项目resources中没有system.properties则最终结果加载到的是B.jar中的system.properties
classpath*
<context:property-placeholder location="classpath*:system.properties" />
举个栗子:A项目中resources中存在system.properties,B项目中resources中也存在system.properties。A项目以jar包形式引入B项目。此时A项目spring配置classpath*:system.properties加载到的是A和B的system.properties
如下图(我是引入的两个jar都有同名配置文件):
这里大家可能会有疑问,同时引入了两个同名配置文件,如果两个同名配置文件中有同样的配置,比如:
A项目system.properties
milo=a
milo1=a
B项目system.properties
milo=b
milo1=b
那么最终引用的是哪个?我这边最终实验结果是根据spring加载顺序倒叙加载,直到找到匹配的值。而且值与值之间不影响,每次都按加载顺序倒叙查找。即:如果spring加载顺序为A->B ,那么最终取到的值为:milo=b milo1=b。而且spring在取milo和milo1时都是按照spring加载顺序倒叙取得。大家可以通过三个配置文件情况做下试验。对spring熟悉的同学也可找下源码看一下。
建议大家没有特殊需求不要用classpath*,因为加载越多效率越低
file
<context:property-placeholder location="file:/Users/mac/Desktop/IDEA Project/dubbodemo/dubbodemo-user-service/lc.properties" />
通过file我们可以将配置文件放在项目外边,这个标签也完美的解决文章最前边提到的运维人员更改jar包配置的问题。当然war包也可以这样配置。
通过JVM传参指定配置文件路径
最后讲一下结合JVM如何动态指定参数,配置文件路径最终一定是运维人员决定,所以配置文件参数我们要动态赋值,这里我们通过java -D{你的参数}方式将参数传给JVM
#/Users/mac/Desktop/lc.properties 这个参数大家注意中间不要有空格(比如文件夹名字有空格是可以的),不然会被理解为两个参数 java -jar -Dlc=/Users/mac/Desktop/lc.properties dubbodemo-user-service-1.0-SNAPSHOT.jar
Spring中引入lc这个参数
<context:property-placeholder location="file:${lc}" />
这样lc就可以传递给spring了,spring中引用方法和我们写在我们自己写的system.properties中的参数没什么区别。但是JVM的参数优先级别要比我们自己写的system.properties的优先级别要高。这里大家注意有限级别越高越先执行,匹配到了就不再向下执行了。environmentProperties,systemProperties,systemEnvironment的优先级别要大于用户的配置。哪怕用户配置<context:property-placeholder order="1"/>也是nvironmentProperties,systemProperties,systemEnvironment的优先级别更高。