Maven学习心得,原文地址:https://www.yiibai.com/maven
7.17
添加了Maven的文件夹结构、依赖机制
创建一个Java项目及其目录结构
创建一个web项目及其目录结构
打包以及运行最基本的jar包7.18
2. Maven的中央资源库
http://repo1.maven.org/maven/,改版后目录浏览不能使用,但是依然可以从这个网址获得依赖,同时可以通过https://search.maven.org/或http://repo1.maven.org/maven2来访问目录浏览。
3. 获取依赖
在Maven中,当你声明的库不存在于本地存储库中,也没有不存在于Maven中心储存库,该过程将停止并将错误消息输出到 Maven 控制台。
例:
<!--获取org.jvnet.localizer依赖,但是由于org.jvnet.localizer只适用于Java.net资源库,
所以将因为找不到依赖报错-->
<dependency>
<groupId>org.jvnet.localizer</groupId>
<artifactId>localizer</artifactId>
<version>1.8</version>
</dependency>
<!--声明Java.net储存库,告诉Maven来获得Java.net的依赖-->
<repositories>
<repository>
<id>java.net</id>
<url>https://maven.java.net/content/repositories/public/</url>
</repository>
</repositories>
声明库后,Maven的依赖库查找顺序变为:
- 在 Maven 本地资源库中搜索,如果没有找到,进入第 2 步,否则退出。
- 在 Maven 中央存储库搜索,如果没有找到,进入第 3 步,否则退出。
- 在java.net Maven的远程存储库搜索,如果没有找到,提示错误信息,否则退出。
Maven中央仓库以外的远程仓库
Java.net资源库:
<project ...>
<repositories>
<repository>
<id>java.net</id>
<url>https://maven.java.net/content/repositories/public/</url>
</repository>
</repositories>
</project>
JBoss Maven资源库:
<project ...>
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
</project>
4. Maven依赖机制
- 知道所需的包的Maven坐标,例如:
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
- 由Maven自动下载jar包,如果version标签被忽略,则当有新的版本时它会自动升级。
- Maven坐标转换成pom.xml文件,例如:
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
- 当Maven编译或构建时,jar包会自动下载,并将其加入Maven本地存储库。
- 所有由Maven管理。
5. 将自定义的jar包添加到本地存储库
以kaptcha为例。
1. mvn安装
安装命令:
mvn install:install-file -Dfile=c:\kaptcha-{version}.jar
-DgroupId=com.google.code
-DartifactId=kaptcha
-Dversion={version}
-Dpackaging=jar
- -Dfile:文件位置
- -DgroupId:项目创建者或组织的唯一标识符
- -DartifactId:项目的唯一标识符
- -Dversion:版本号
- -Dpackaging:可选,默认为jar
安装kaptcha-2.3.2.jar的命令:
mvn install:install-file -Dfile=c:\kaptcha-2.3.2.jar -DgroupId='com.google.code' -DartifactId=kaptcha -Dversion='2.3.2' -Dpackaging=jar
成功提示:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom ---
[INFO] Installing d:\kaptcha-2.3.2.jar to C:\Users\话神闲\.m2\repository\com\google\code\kaptcha\2.3.2\kaptcha-2.3.2.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.020 s
[INFO] Finished at: 2019-07-17T13:55:42+08:00
[INFO] ------------------------------------------------------------------------
PS:当带"."的值(例如版本号的2.3.2和组织标识符的com.google.code)不用引号包起来时,有可能报错。
报错信息:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.109 s
[INFO] Finished at: 2019-07-17T14:05:18+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] **The goal you specified requires a project to execute but there is no POM in this directory (C:\Users\话神闲\Downloads). Please verify you invoked Maven from the correct directory. -> [Help 1]
2. 配置pom.xml
在pom.xml中声明刚才安装到本地存储库的jar包的坐标
<dependency>
<groupId>com.google.code</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3</version>
</dependency>
3. 完成
完成以上步骤便可从Maven本地存储库检索了。
6. 创建Java项目
- 从Maven模板创建一个项目
创建命令:
mvn archetype:generate
-DgroupId={project-packaging}
-DartifactId={project-name}
-DarchetypeArtifactId=maven-archetype-quickstart
-DinteractiveMode=false
- -DgroupId:组织唯一标识符
- -DartifactId:项目唯一标识符
- -DarchetypeArtifactId:使用的模板
- -DinteractiveMode:是否需要和用户交互以获得输入
PS:
1. 同上面安装jar包到本地存储库的命令相同,带"."的参数(如组织唯一标识符)可能会引起报错,因此最好用引号包住。
2. 创建项目时可能会停留在下面这一步很久
[INFO] Generating project in Batch mode
解决方案是:
- 将http://repo1.maven.org/maven2/archetype-catalog.xml的archetype-catalog.xml文件下载并放到 ~.m2\repository\org\apache\maven\archetype\archetype-catalog{版本号}"文件夹下。然后在创建项目的命令后增加新的参数-DarchetypeCatalog=local,表示从本地读取这个文件
- 第二种方法更简单,只要添加-DarchetypeCatalog=internal参数就可以,而且不需要添加-DinteractiveMode=false
7. Maven Java项目目录布局
HelloWorld
├──src
│ ├──main
│ │ └──java
│ │ └──mycompany
│ │ └──App.java
│ └──test
│ └──java
│ └──mycompany
│ └──App.java
└──pom.xml
Directory | Description |
---|---|
src/main/java | Application/Library sources |
src/main/resources | Application/Library resources |
src/main/filters | Resource filter files |
src/main/webapp | Web application sources |
src/test/java | Test sources |
src/test/resources | Test resources |
src/test/filters | Test resource filter files |
src/it | Integration Tests (primarily for plugins) |
src/assembly | Assembly descriptors |
src/site | Site |
LICENSE.txt | Project's license |
NOTICE.txt | Notices and attributions required by libraries that the project depends on |
README.txt | Project's readme |
8. 打包jar
在终端使用以下命令:
mvn package
在target目录生成jar文件
此时文件目录结构:
HelloWorld
├──src
│ ├──main
│ │ └──java
│ │ └──mycompany
│ │ └──App.java
│ └──test
│ └──java
│ └──mycompany
│ └──App.java
├──target
└──pom.xml
9. 运行jar包
java -cp HelloWorld.jar mycompany.App
这里要用cp(classpath)指定运行的主类名称
Maven导出的包中:
- 如果指定了主类,可以用Java -jar xxx.jar
- 如果未指定主类,可以用Java -cp xxx.jar 包名.主类
10. Maven创建Web项目
命令:
mvn archetype:generate
-DgroupId=mycompany
-DartifactId=myapp
-DarchetypeArtifactId=maven-archetype-webapp
-DinteractiveMode=false
Web项目目录布局:
MyApp
├──src
│ └──main
│ ├──webapp
│ │ ├──WEB-INF
│ │ │ └──web.xml
│ │ └──index.jsp
│ └──resources
└──pom.xml
Directory | Description |
---|---|
src/main/java | Application/Library sources |
src/main/resources | Application/Library resources |
src/main/filters | Resource filter files |
src/main/webapp | Web application sources |
src/test/java | Test sources |
src/test/resources | Test resources |
src/test/filters | Test resource filter files |
src/it | IntegrationTests (primarily for plugins) |
src/assembly | Assembly descriptors |
src/site | Site |
LICENSE.txt | Project's license |
NOTICE.txt | Notices and attributions required by libraries that the project depends on |
README.txt | Project's readme |
11. 使用IDEA创建Maven项目
12. Maven Pom
POM:项目对象模型
POM的例子
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yiibai.project-group</groupId>
<artifactId>project</artifactId>
<version>1.0</version>
<project>
POM的三个必填字段:groupId、artifactId、version。
在库中的项目符号:groupId:artifactId:version。
根元素:project
节点 | 描述 |
---|---|
groupId | 这是项目组的编号,这在组织或项目中通常是独一无二的。 例如,一家银行集团com.company.bank拥有所有银行相关项目。 |
artifactId | 这是项目的ID。这通常是项目的名称。 例如,consumer-banking。 除了groupId之外,artifactId还定义了artifact在存储库中的位置。 |
version | 这是项目的版本。与groupId一起使用,artifact在存储库中用于将版本彼此分离。 例如:com.company.bank:consumer-banking:1.0,com.company.bank:consumer-banking:1.1 |
有效POM:
超级POM:被所有的POM继承的,包含继承默认值的基础POM。
有效的POM:超级POM加项目配置的配置
子pom.xml会完全继承父pom.xml中所有的元素,而且对于相同的元素,一般子pom.xml中的会覆盖父pom.xml中的元素,但是有几个特殊的元素它们会进行合并而不是覆盖。这些特殊的元素是:
- dependencies
- developers
- contributors
- plugin列表,包括plugin下面的reports
- resources
查看有效POM的命令:
mvn help:effective-pom
PS D:\home\mavenworkspace\HelloMaven> mvn help:effective-pom
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------------< com.ltc:HelloMaven >-------------------------
[INFO] Building HelloMaven 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.2.0:effective-pom (default-cli) @ HelloMaven ---
[INFO]
Effective POMs, after inheritance, interpolation, and profiles are applied:
<?xml version="1.0" encoding="GBK"?>
<!-- ====================================================================== -->
<!-- -->
<!-- Generated by Maven Help Plugin on 2019-07-18T14:47:48+08:00 -->
<!-- See: http://maven.apache.org/plugins/maven-help-plugin/ -->
<!-- -->
<!-- ====================================================================== -->
<!-- ====================================================================== -->
<!-- -->
<!-- Effective POM for project 'com.ltc:HelloMaven:jar:1.0-SNAPSHOT' -->
<!-- -->
<!-- ====================================================================== -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ltc</groupId>
<artifactId>HelloMaven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>HelloMaven</name>
<url>http://maven.apache.org</url>
<!--省略pom.xml文件细节-->
</project>
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.881 s
[INFO] Finished at: 2019-07-18T14:47:49+08:00
[INFO] ------------------------------------------------------------------------
13. 快照版本和正式版本
SNAPSHOT版本:
- 一个SNAPSHOT版本可以多次发布(deploy)到仓库中,也就是说,一个SNAPSHOT是一系列版本的集合,而HEAD指针总是指向最新的版本。因此,当有新的SNAPSHOT版本发布时,由于本地的版本和实际HEAD指针所指向的版本不同,因此Maven会自动下载新版本的SNAPSHOT版本。
SNAPSHOT文件夹里用来保存历史发布的文件内容
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>ltc</groupId>
<artifactId>SnapShot</artifactId>
<version>1.0-SNAPSHOT</version>
<versioning>
<snapshot>
<localCopy>true</localCopy>
</snapshot>
<lastUpdated>20190718152857</lastUpdated>
<snapshotVersions>
<snapshotVersion>
<extension>jar</extension>
<value>1.0-SNAPSHOT</value>
<updated>20190718152857</updated>
</snapshotVersion>
<snapshotVersion>
<extension>pom</extension>
<value>1.0-SNAPSHOT</value>
<updated>20190718152857</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
</metadata>
- SNAPSHOT写法:
<version>{version}-SNAPSHOT</version>
- Maven在工作时,会自动下载最新版的SNAPSHOT版本,但是也可以通过任何命令加上 -U 参数来强制获取最新的SNAPSHOT。
mvn clean -U
RELEASE版本:
- 一个正式版本对应的就是一个特定的版本,版本名称后缀为:-RELEASE。正式版本,不允许覆盖。如果一定要提交,要么删除私服上已经存在的版本,要么升级版本重新发布。
- RELEASE写法:
<version>{version}-RELEASE</version>
总结:
RELEASE版本 -> 稳定性
SNAPSHOT版本 -> 方便性
快照和正式版本的选择:
- 在开发期间,方便性优于稳定性。
- 生产环境,稳定性重于一切。
例子:
模块A依赖模块B的1.0版本,而假如B假如发布新特性或者修复了旧版本的漏洞,则必须发布新的1.1版本,并通知所有依赖B-1.0版本的模块更新依赖。
但是如果模块A依赖的是模块B的1.0-SNAPSHOT版本,则B修复完漏洞,只需再次发布1.0-SNAPSHOT版本,而A模块就能获得了最新版本的B模块。
14. 版本区间
范围 | 说明 |
---|---|
1.0 | version >= 1.0 * The default Maven meaning for 1.0 is everything (,) but with 1.0 recommended. Obviously this doesn't work for enforcing versions here, so it has been redefined as a minimum version. |
(,1.0] | version <= 1.0 |
(,1.0) | version < 1.0 |
[1.0] | version == 1.0 |
[1.0,) | version >= 1.0 |
(1.0,) | version > 1.0 |
(1.0,2.0) | 1.0 < version < 2.0 |
[1.0,2.0] | 1.0 <= version <= 2.0 |
(,1.0],[1.2,) | version <= 1.0 or version >= 1.2. Multiple sets are comma-separated |
(,1.1),(1.1,) | version != 1.0 |
测试:
本地库版本有:
- 0.9
- 1.0
- 1.0-SNAPSHOT
- 1.1
- 1.9
- 2.0
- 2.0-SNAPSHOT
- 2.1-SNAPSHOT
- 2.2-SNAPSHOT
<version>(1,)</version>
导入的为2.3-SNAPSHOT
----------------------------------
<version>(1,2]</version>
导入的为2.0
----------------------------------
<version>(1,2)</version>
导入的为2.0-SNAPSHOT
----------------------------------
<version>(,1)</version>
导入的为1.0-SNAPSHOT
----------------------------------
<version>(,0.99)</version>
导入的为0.9
----------------------------------
<version>(,0.9) , (2.3,)</version>
报错,找不到符合范围的包
----------------------------------
15. 移除依赖包
当想导入的jar包依赖于另一个jar包,而实际上并没用到这个包又或者自动导入的这个包并不稳定而需要重新导入另一个稳定版本时,可以将其移除。
<dependency>
<groupId>ltc</groupId>
<artifactId>B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>ltc</groupId>
<artifactId>A</artifactId>
</exclusion>
</exclusions>
</dependency>
如上,其中B部分功能依赖于A,当前项目C依赖于B,当未使用到B中依赖A的功能时可以把A包移除。同样的,如果B中自动导入的A并不稳定,也可通过这个方式先将其移除,然后将导入A的稳定版本。
16. Profiles
通过定义多个profile,可以分别为不同的环境配置不同的设置,然后在需要激活的时候通过特定方法激活特定的配置。
项目结构如下:
ProfileTest
├──src
│ ├──main
│ │ ├──java
│ │ │ └──ltc
│ │ │ └──App.java
│ │ └──resources
│ │ ├──dev
│ │ │ └──application-dev.properties
│ │ ├──prod
│ │ │ └──application-prod.properties
│ │ └──test
│ │ └──application-test.properties
│ └──test
│ └──java
│ └──ltc
│ └──App.java
└──pom.xml
其中,pom.xml文件配置如下:
<!--最简单的profiles配置-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ltc</groupId>
<artifactId>ProfileTest</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>ProfileTest</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<!--每个profile标签都定义了一个profile-->
<profile>
<!--id可以用来表示一个profile,而且可以在打包时通过-P{id}参数来选择激活的profile-->
<id>dev</id>
<!--自定义属性,在profile激活时生效-->
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
<!--另一种激活profile的方法,当使用maven命令时未指定所激活的profile时,默认激活该profile-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<env>test</env>
</properties>
</profile>
</profiles>
<build>
<resources>
<!--通过resource标签里的directory来选择要加入打包的文件夹-->
<resource>
<directory>src/main/resources/${env}</directory>
</resource>
</resources>
</build>
</project>
激活profile:
- 通过<activeByDefault>标签所在的profile来激活,这时无需指定profile
mvn compile
编译后的target文件夹:
target
├──classes
│ ├──ltc
│ │ └──App.class
│ └──application-prod.properties
└──maven-status
可见,被<activeByDefault>所指定的properties文件被添加到了目标文件夹中。
- 通过编译时的-P参数来激活
mvn clean compile -Pdev
编译后的target文件夹:
target
├──classes
│ ├──ltc
│ │ └──App.class
│ └──application-dev.properties
└──maven-status
由于在编译时指定了profile,所以默认的profile并没有被添加到target文件夹 。
进阶配置:
-
filter的配置:
原理:
用build-filters-filter里选择的文件中的键值对填充resources-resource-directory-includes-include选择的文件中的${...}占位符。
配置:
该项目文件结构:
ProfileTest
├──src
│ ├──main
│ │ ├──java
│ │ └──resources
│ │ ├──dev
│ │ │ └──application-dev.properties
│ │ ├──prod
│ │ │ └──application-prod.properties
│ │ ├──test
│ │ │ └──application-test.properties
│ │ └──application.properties
│ └──test
└──pom.xml
pom.xml:
<filters>
<filter>src/main/resources/${env}/application-${env}.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources/</directory>
<!--filtering,当其值为true时,才会填充下面include包含的文件-->
<filtering>true</filtering>
<!--includes,用于指定要加入打包的文件-->
<includes>
<include>application.properties</include>
</includes>
</resource>
application.properties:
url=${url}
username=${username}
password=${password}
application-dev.properties:
url=dev
username=name-dev
password=password-dev
prod和test配置文件的内容与dev基本相似,编译打包后,application.properties中用${...}占位符表示的地方,会用filter选择的文件进行填充,因此编译打包后的application.properties的内容为:
url=dev
username=name-dev
password=password-dev
此外,如果需要打包的配置文件中(application.properties),有filter文件中未出现的键值对,那么${...}将保持原样不会被替换。
参考资料:
maven profile动态选择配置文件
maven(三)最详细的profile的使用
maven 根据profile,resources,filters来区分部署环境
17. 继承
一个项目可以继承另一个项目的依赖,前者被称为子项目,后者被称为父项目。
在父项目声明的依赖,在子项目中可以直接获得。父项目也可以通过<dependencyManagement>标签来管理依赖,该标签下的依赖子项目默认不继承,但可通过在子pom.xml声明groupId和artifactId即可继承父项目中的依赖。
父pom配置:
--------------------------------------------------------------------------------------------
<modelVersion>4.0.0</modelVersion>
<groupId>ltc</groupId>
<artifactId>Parent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<name>Parent</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
</dependencyManagement>
--------------------------------------------------------------------------------------------
子pom配置:
--------------------------------------------------------------------------------------------
<modelVersion>4.0.0</modelVersion>
<artifactId>Child1</artifactId>
<url>http://maven.apache.org</url>
<parent>
<artifactId>Parent</artifactId>
<groupId>ltc</groupId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
--------------------------------------------------------------------------------------------
可以看到,子项目中不需要配置就直接继承了父项目在dependencies中声明的junit,通过配置commons-io和log4j的groupId和artifactId也获取到了父项目中对应版本的jar包。
PS:
- 父项目需要打包格式为pom
- 父项目需要安装或者指定<relativePath>,否则子项目将找不到父项目
18. 聚合
- 创建方式
父模块:创建普通的Maven项目,打包方式为pom
子模块:命令行模式下,在父模块目录下,创建Maven项目。IDEA中在父模块上右键新建Module。
父模块pom:
<modelVersion>4.0.0</modelVersion>
<groupId>ltc</groupId>
<artifactId>Combine</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>Combine</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<modules>
<module>Son1</module>
<module>Son2</module>
</modules>
子模块pom:
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ltc</groupId>
<artifactId>Combine</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>ltc</groupId>
<artifactId>Son1</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Son1</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
父模块在module声明了子模块的名称,子模块也声明了父模块,不过与继承不同的是父模块不用安装到存储库中,子模块也能找到对应父模块。同时子模块也可以继承父模块的依赖。