本来打算接着上文直接写登录功能的,顺便介绍下SpringSecurity,但是SpringSecurity和Spring以及SpirngBoot是如何衔接的又是个较为复杂的事情。看了下SpringBoot的启动过程之后,发现里面很多地方用到了类加载器以及Classpath等。其实如果没用过Eclipse写过传统的java项目或者web项目,经常会对ClassPath,根目录,类加载器,jar包格式,war包格式等迷糊,所以一方面自己总结下,一方面也给大家扫下盲。
一、Jar包和War包
通常来说java可以分为普通项目和web项目。普通项目的运行我们需要自己写一个main函数,然后以这个main函数作为入口启动项目。而web项目则需要交给servlet容器去启动,通常是tomcat或者jetty。web项目的启动过程是一个十分复杂的事情,不过目前我们不用关心细节,毕竟是一篇扫盲文章。
1、什么是打包
除了SDK以外,一个项目我们写完之后都需要部署到linux服务器上运行。但是并不是直接部署项目源码到linux上,而是经过编译,打包后的"包"。整个过程我们称之为构建(build),后端常用的构建工具也就那几个,当然最简单也最普遍的就是maven了。编译和打包对应到maven命令则分别是compile和package。
mvn compile可以将.java文件编译成.class文件,也就是将源码转化为字节码,即一次编译到处运行的编译过程。
mvn package则将编译后的.class文件和相关的资源配置如.properties配置文件.xml配置文件按照约定或者我们自己的配置整合到一个jar包或者war包中。至于如何整合,以及最终是jar包还是war包,要根据我们的配置来决定,maven的配置文件就是项目根目录下的pom.xml文件。
所以打包可以有两个概念,术语来说就是指mvn package这一步,如果是我们口头上常说的打包其实就是整个构建过程。
2、如何打包
目前市场上最常用的构建工具要属maven(其他的还有ant,gradle等就不介绍了)。
首先我们使用idea创建一个maven项目:File -> New -> Project,然后一路点next就行
这样我们就创建了一个最基本的maven项目,如下图所示
可以看到,我们通过maven创建的项目,已经有了自己默认的目录结构。这里不得不提到这个目录结构的由来,即maven的核心思想:约定先行于配置。只要知道了这个,maven的神秘面纱就基本揭开了。
maven为什么要提供这样一个默认的结构?这个目录结构又有什么用呢?
maven给我们提供的这个默认的结构其实就是对我们的约定,即认为我们的项目目录结构就是这个样子,并没有自己的改动。如果我们遵守了这个约定,则我们在不需要自己额外配置的情况下,可以对项目进行正确的构建。
因为jar和war都是有着严格格式要求的,所以正确的构建就是指打包后的项目目录格式能够满足规定的格式要求。综合起来就是如果我们的项目结构和maven约定的结构相同,则我们可以直接使用mvn package进行打包,无需在pom.xml文件中进行额外配置。当然如果我们想应用自己的目录结构,也可以在pom.xml文件中配置相关内容,让项目正确构建。pom.xml就是maven的相关配置文件。
JAR包:
用刚才创建的项目,我们先在java目录下创建一个类,然后再运行mvn package就可以进行一次打包:
虽然我们只运行了一个mvn package命令,但是从构建日志上来看,似乎运行了很多插件。没错,mvn package并不只是运行package插件,而且根据maven的插件生命周期运行package和它之前的所有插件。
package之前最重要的一步就是compile,打包的也是编译后的文件夹。图上有个黄色的target文件夹就是编译后的文件。而这里有个classes目录,就是所谓的ClassPath,这个概念之后我们会经常遇到,java项目所有的相关内容编译后都会直接放在ClassPath下。
target是编译后的目录,但仍需要将编译后的文件夹转化为jar文件。我们打开打包后的jar包。
可以看到target/classes目录下的内容直接放到了jar包的根目录,并且多了个META-INF文件夹。至于META-INF文件夹有什么作用,暂且可以不用知道,不过我们目前可以确定的就是,ClassPath对应着Jar包的根目录,对应着编译后的target的classes目录。
WAR包:
和jar包不同但经常使用到的就是web项目打包后的war包了。我们重新创建一个项目,并且使用maven提供的约定好的web项目的目录结构:maven-archetype-webapp
创建完成的项目基础结构如下
我们可以发现main目录下还缺少一个java目录,我们需要自己添加一个java目录,并且设置成Root目录,resources目录设置成Source目录,然后创建一个java类,至此一个基本的web模型已经创建完成了。
接下来就是打包测试,我们运行mvn package命令:
如同普通java项目一样,项目结构中多了个编译后的target文件夹。只不过我们需要注意两个子文件夹,分别是classes和web。classes文件夹下存放的依然是编译后的.class文件,而原来的webapp目录编译后则变成了web目录,并且将WEB-INF目录保留了下来。另外你会发现,classes文件夹在WEB-INF下也有一个。这两个classes文件夹是一模一样的。我们来打开war包看看:
和web目录一模一样。所以我们可以得出一个结论就是:ClassPath在web项目中就是/WEB-INF/classes目录。
另外无论是java项目还是javaweb项目,都会有个resources文件夹,你试一下就可以知道,这个目录下的内容打包后都会被复制到ClassPath目录下。
通常上面的测试我们可以了解到,jar包和war包的固定格式。通常来说我们不需要自己去打破约定,按照自己的方式设置项目目录,因为项目毕竟是很多人在同时完成,还是需要有一定的标准。
二、资源文件的加载
通过上面的介绍我们现在已经很清楚什么是ClassPath路径了对吧。下面我们来了解一下Java项目中资源文件的加载。
Java项目中加载资源文件通常有两种方式:1、Class.getResource(String path)
2、Class.getClassLoader().getResource(String path)
这两种方式有一些区别,首先来看1:
可以看出,如果是绝对路径,则绝对路径的起点为ClassPath,如果为相对路径,则路径的起点为该类的所在目录,也就是图中的TestClass所在的目录。
然后来看2,依旧是上面那个项目结构:
可以看出如果使用类加载器加载资源,则只能填入相对路径,且路径的起点一定是ClassPath,而且跟这个类在哪没有任何关系。
三、环境变量相关资源
在了解ClassLoader体系之前我们应该先了解一下我们刚学Java时最头疼的问题,环境变量。
大家对JDK和JRE一定不陌生,其中JRE是Java的运行环境,而JDK包含了JRE。
JDK比JRE多出来的东西主要就是bin目录下的一些exe程序,提供给我们一些强大的开发工具。常用的有javac、java、javap等。
回想一下,学习Java的时候我们配置过三个环境变量:
JAVA_HOME:JDK的安装目录
CLASS_PATH:指向系统JAVA_HOME所在路径的lib文件夹或者其下面的jar包
PATH:添加到PATH路径下的exe文件,可以在cmd中直接使用。所以通常是将JAVA_HOME所在路径的bin文件夹添加到PATH,这样就能在cmd命令行中直接使用java、javac等命令了。我们在学习Mysql时配置Path同样是这个原因。通过这里我们又学到了一个CLASS_PATH,只不过这里指的是系统变量。
最后附上一条个人认为目前看到过写的最全的一个关于类加载器的一篇文章,不过文章中有些地方有点错误,比如引导类加载器加载的并不是JAVA_HOME路径下的类,而是JRE_HOME下的类。
https://blog.csdn.net/javazejian/article/details/73413292blog.csdn.net