前言
Spring是怎么找到resources下面的配置文件的(一)? - 掘金 (juejin.cn) 里面我们探寻了在spring中,ClassPathResource封装了我们classpath类资源,然后容器BeanFactory通过通过java类加载机制在java.class.path路径中的资源。
Java.class.path是系统属性的一个key,通过System.getProperty("java.class.path"),类加载器就能获取加载类的路径。
这里我们探寻一下System.getProperty("java.class.path")获取到的系统属性是怎么来的或者说是怎么进行赋值的呢?
我们说一下结论: Java.class.path这个系统属性是我们运行程序的时候,也就是java.exe -classpath时候指定的,指定的classpath就是两类,一个就是我们项目compile编译生成的target/classes目录,另一个就是我们项目pom包里面的依赖,也就是lib。
我都是用的idea开发maven项目,直接点击执行的时候,在console中会有项目启动的指令!由于现在idea的便捷,我往往都忽略了这里面的参数
探究原理
首先我们了解一下什么是classpath和java.class.path
什么是classpath 与 java.class.path?
启动 java 程序时,会根据 classpath 拼接 java.class.path 。 Java.class.path是由classpath拼接来的
在IDE中运行Java程序,IDE自动传入的-classpath
参数是当前工程的bin
目录和引入的jar包。 而classpath有两种设置方式,一种是在系统环境变量中设计classpath环境变量,不推荐,因为会污染整个系统环境;另一种是在启动jvm时设置classpath变量。
在idea中运行java程序,会自动传入的-classpath参数是当前工程的bin目录和引入的jar包
Idea中的classpath呢?
很好理解,classpath就是我们编写的java类文件编译后生成的字节码class文件目录,
我们打开项目结构,可以看到默认就是我们的老朋友*\target\classes,而且支持自定义!
那些文件会被编译进*\target\classes中呢?
我们打开项目结构->modules->Sources,右边列出了4个项目中的文件类型:
Source Folders:表示的都是代码源文件目录,生成的class文件会输出到target->classess文件夹中,但是里面的源文件不会复制到target->classes文件夹中
Test Source Folders: 表示的都是测试代码源文件目录,生成的class文件同样会输出到target->classess文件夹中,并且里面的源文件不会复制到target->classes文件夹中
Resource Folders: 表示的都是资源文件目录,这些目录里面的文件会在代码编译运行被直接复制到target->classess文件夹中
Excluded Folders:表示的是target文件夹生成的位置,target是IDEA编译后的一些class信息存放地,里面有子目录target->classes来存储编译后的字节码。
target->classes即为classpath路径位置,任何我们需要在classpath:前缀中获取的资源都必须在target->classes文件夹中找到,否则将出现java.io.FileNotFoundException的错误信息。
扩展
Jar包目录结构
maven install,将项目打包成jar包,包含所有三方依赖的jar。它与传统jar包最大的不同是包含了一个lib目录和内嵌了web容器。 可以看到jar包下就有只有三个目录:BOOT-INF,META-INF,ORG
该目录比使用传统jar命令打包结构更复杂一些,目录含义如下:
- BOOT-INF/classes:目录存放应用编译后的class文件。
- BOOT-INF/lib:目录存放应用依赖的第三方JAR包文件。
- META-INF:目录存放应用打包信息(Maven坐标、pom文件)和MANIFEST.MF文件。
- org:目录存放SpringBoot相关class文件。
点开BOOT-INF,可以发现,我们编译输出在target的classe文件全在这里,并且这里还有我们依赖的lib
Maven编译生成的jar包
普通的jar包
普通的jar包只包含工程源码编译出的class文件以及资源文件,而不包含任何依赖;同时还包括pom文件,说明该包的依赖信息; 在工程pom文件下这样写,可生成普通的jar包:
java
<packaging>jar<packaging>
或者,不写,因为packaging默认打包类型为jar
包含所有依赖的jar包
普通的jar包,只在pom文件中包含依赖信息,而不包含真正的依赖,但同时maven也可以打包生成包含所有依赖的jar文件
maven打包生成的普通jar包,只包含该工程下源码编译结果,不包含依赖内容。同时,maven提供以下方式生成包含所有依赖的jar文件,依赖以class的方式存在;
- 将此plugin添加到pom文件中
java
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<!--这部分可有可无,加上的话则直接生成可运行jar包-->
<!--<archive>-->
<!--<manifest>-->
<!--<mainClass>${exec.mainClass}</mainClass>-->
<!--</manifest>-->
<!--</archive>-->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
如果出现CLASS重名的情况,这时候就要把最新的版本号添加进去即可,
2.在当前项目下执行mvn assembly:assembly, 执行成功后会在target文件夹下多出一个以-jar-with-dependencies结尾的JAR包. 这个JAR包就包含了项目所依赖的所有JAR的CLASS.
3.如果不希望依赖的JAR包变成CLASS的话,可以修改ASSEMBLY插件.
找到assembly在本地的地址,一般是 c:/users/${your_login_name}/.m2/\org\apache\maven\plugins\maven-assembly-plugin\2.4 用WINZIP或解压工具打开此目录下的maven-assembly-plugin-2.4.jar, 找到assemblies\jar-with-dependencies.xml 把里面的UNPACK改成FALSE即可
可运行jar包
maven默认打包生成的jar是不能够直接运行的,因为在jar文件的META-INF/MANIFEST.MF文中没有Main-Class一行,为了生成可执行的jar文件,需要借助maven的插件,maven-shade-plugin,配置该插件如下:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
</transformers>
<artifactSet>
</artifactSet>
<!--<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>-->
</configuration>
</execution>
</executions>
</plugin>
但是我的项目打包的时候只用了如下的配置:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
可以看到,springboot应该做了一些封装,封装的原理后面再分享出来