POM
Project Object Model:项目对象模型。
将 Java 工程的相关信息封装为对象作为便于操作和管理的模型。
是 Maven 工程的核心配置。可以说学习 Maven 就是学习 pom.xml 文件中的配置。
约定的目录结构
现在 JavaEE 开发领域普遍认同一个观点:约定>配置>编码。意思就是能用配置解决的问题就不编码,能基于约定的就不进行配置。而 Maven 正是因为指定了特定文件保存的目录才能够对我们的 Java 工程进行自动化构建。目录结构含义参见 HelloWorld 的描述。
坐标
- 几何中的坐标
- 在一个平面中使用 x、y 两个向量可以唯一的确定平面中的一个点。
- 在空间中使用 x、y、z 三个向量可以唯一的确定空间中的一个点。
- Maven 中的坐标:使用如下 3 个向量在 Maven 的仓库中唯一的确定一个 Maven 工程
- groupId:公司或组织的域名倒序 + 当前项目名称
- artifactId:当前项目的模块名称
- version:当前模块的版本
- 通过坐标到仓库中查找 jar 包
- 将 gav 的 3 个向量连起来:
cn.edu.nuist.maven+HelloWorld+0.0.1-SNAPSHOT
- 以连起来的字符串作为目录结构到仓库中查找:
cn/edu/nuist/HelloWorld/0.0.1-SNAPSHOT/HelloWorld-0.0.1-SNAPSHOT.jar
- 将 gav 的 3 个向量连起来:
- 注意!我们自己的 Maven 工程必须执行安装操作才会进入仓库。安装命令:
mvn install
STS 配置 Maven
导入已存在 Maven 项目:
依赖管理
基本概念
当 A-jar 包需要用到 B-jar 包中的类时,我们就说 A 对 B 有依赖。例如:commons-fileupload-1.3.jar 依赖于 commons-io-2.0.1.jar。
通过 ↑ Maven 工程我们已经看到,当前工程会到本地仓库中根据坐标查找它所依赖的 jar 包。
配置的基本形式是使用 <dependency>
指定目标 jar 包的坐标。例如:
<dependencies>
<dependency>
<!-- 坐标 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<!-- 依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>
直接依赖和间接依赖
如果 A 依赖 B,B 依赖 C,那么 A→B 和 B→C 都是直接依赖,而 A→C 是间接依赖。
依赖的范围
当一个 Maven 工程添加了对某个 jar 包的依赖后,这个被依赖的 jar 包可以对应下面几个可选的范围:
- compile [默认]
- main 目录下的 Java 代码可以访问这个范围的依赖
- test 目录下的 Java 代码可以访问这个范围的依赖
- 部署到 Tomcat 服务器上运行时要放在 WEB-INF 的 lib 目录下
- 例如:对 HelloWorld 的依赖。主程序、测试程序和服务器运行时都需要用到
- test
- main 目录下的 Java 代码不能访问这个范围的依赖
- test 目录下的 Java 代码可以访问这个范围的依赖
- 部署到 Tomcat 服务器上运行时不会放在 WEB-INF 的 lib 目录下
- 例如:对 junit 的依赖。仅仅是测试程序部分需要
- provided
- main 目录下的 Java 代码可以访问这个范围的依赖
- test 目录下的 Java 代码可以访问这个范围的依赖
- 部署到 Tomcat 服务器上运行时不会放在 WEB-INF 的 lib 目录下
- 例如:servlet-api 在服务器上运行时,Servlet 容器会提供相关 API,所以部署的时候不需要
- runtime [了解]
- main 目录下的 Java 代码不能访问这个范围的依赖
- test 目录下的 Java 代码可以访问这个范围的依赖
- 部署到 Tomcat 服务器上运行时会放在 WEB-INF 的 lib 目录下
- 例如:JDBC 驱动。只有在测试运行和在服务器运行的时候才决定使用什么样的数据库连接
- 其他:import、system ...
依赖的传递性
当存在间接依赖的情况时,主工程对间接依赖的 jar 可以访问吗?这要看间接依赖的 jar 包引入时的依赖范围——只有依赖范围为 compile 时可以访问。例如:
依赖的原则
这里“声明”的先后顺序指的是 <dependency>
配置的先后顺序。
依赖的排除
有的时候为了确保程序正确可以将有可能重复的间接依赖排除。请看如下的例子:
- 假设当前工程 A 依赖于 C,而 C 依赖于 spring-core-4.0.0.RELEASE.jar
- 故 spring-core-4.0.0.RELEASE.jar 依赖 commons-logging-1.1.1.jar 对于A 来说是间接依赖
- 当前工程 A 直接依赖 commons-logging-1.1.2.jar
- A-pom.xml 加入 exclusions 配置后可以在依赖 spring-core-4.0.0.RELEASE.jar 的时候排除版本为 1.1.1 的 commons-logging 的间接依赖
<dependency>
<groupId>cn.edu.nuist.maven</groupId>
<artifactId>C</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 依赖排除 -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<!-- 根据路径最短者优先原则,就算不设置依赖排除,也会用自己配的 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.2</version>
</dependency>
</dependency>
统一管理 jar 版本
以对 Spring 的 jar 包依赖为例:Spring 的每一个版本中都包含 spring-core、spring-context 等 jar 包。我们应该导入版本一致的 Spring jar 包,而不是使用 4.0.0 的 spring-core 的同时使用 4.1.1 的 spring-context。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
问题是如果我们想要将这些 jar 包的版本统一升级为 4.1.1,是不是要手动一个个修改呢?显然,我们有统一配置的方式:
<!-- 这样一来,进行版本调整的时候只改一改 ↓ 就行了 -->
<properties>
<spring.version>4.1.1.RELEASE</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>