Maven
Maven
Maven介绍
Apache Maven是一个项目管理工具,基于POM(project object model)文件,Maven可以实现对Java项目的构建和管理。
由于Ant过于灵活,构建脚本长而难以维护, Maven团队认识到标准化项目布局和统一构建生命周期的必要性。 Maven选择约定优于配置的思想。
例如,使用标准目录, 这使得开发人员可以轻松的知道去哪里找什么类型的文件,例如,Java应用程序源代码的目录是src/main/java。
标准目录
Maven项目标准目录结构如下:
a-maven-project
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ └── resources
│ └── test
│ ├── java
│ └── resources
└── target
pom.xml
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.junnanwu</groupId>
<artifactId>maven_project</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
...
</properties>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
-
packaging项目的打包方式,默认为
jar -
dependencies/dependency*使用
<dependency>声明一个依赖后,Maven就会自动下载这个依赖包并把它放到classpath中。-
groupId类似Java包名,通常是公司或者组织的名字
-
artifactId项目的名称
-
version
一个maven工程就是由这3个变量作为唯一标识,引用其他第三方库的时候,也是通过这三个变量确定的。
-
scope指定依赖作用域
-
complle编译时需要,最常见的类型,默认也是这种,Maven会把这种类型的依赖直接放入classpath
-
test编译Test时需要,例如JUnit
-
runtime编译时不需要,但是运行时需要用到,例如mysql驱动
-
provided编译时需要,但是运行的时候不需要,例如Serverlet API,编译的时候,需要,但是运行的时,Servlet服务器内置了相关的Jar,所以运行的时候,不需要。
-
-
注意:
当我们打包的时候要想打包某个依赖的时候,可以使用provided
Maven配置
加载setting的顺序
优先加载用户目录下的 setting.xml,如果没有,则找全局的setting.xml,即pom.xml > /home_dir/.m2/settings.xml > /maven_dir/conf/settings.xml。
依赖管理
引入依赖
当我们需要引入依赖的时候,可以去mvean中央仓库进行搜索,Maven维护了一个中央仓库,所有的第三方库将自身的jar及相关信息上传至中央仓库,Maven从中央仓库下载到本地(用户目录的.m2目录)。
maven解决了依赖管理问题,例如,我们的项目依赖abc这个jar包,而abc又依赖xyz这个jar包,当我们声明了abc的依赖时,Maven自动把abc和xyz都加入了我们的项目依赖。
查看依赖树
$ mvn dependency:tree
加上-Dverbose会显示更详细的信息。
设置仓库镜像
由于Maven官方仓库下载速度太慢,我们可以使用国内的景象进行替换,详见此。
在settings.xml文件中添加如下内容:
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
注意:
-
<mirrorOf>标签指的被代理的仓库,Maven中央仓库默认的id为central,所以上面的配置表示,当Maven尝试访问中央仓库的时候只会访问该镜像 -
<mirrorOf>如果设置为*,则意味着代理所有仓库如果你想让项目只访问你的私服,而不访问中央仓库,那么可以私服额外设置
<mirror>,并设置<mirrorOf>*</mirrorOf>。 -
一个存储库最多只能有一个镜像仓库,如果有多个,maven不会将多个聚合在一起,只会选择第一个匹配项
-
可以通过如下命令查看镜像和仓库的生效顺序
$ mvn help:effective-settings
更多详见官方文档。
Maven生命周期
生命周期
Maven三套独立的生命周期 (lifecycle),Maven的步骤必须按照这个生命周期来,而Ant的每个步骤都需要你去手工定义,这也是Maven比较简单的一个原因。
使得我们在不同的项目中,也能使用相同的命令mvn clean install。
预定义的三个独立的生命周期分别是:
-
default部署项目相关流程 -
clean清理项目(注意:与上面的不是一个生命周期)
-
site网站发布相关
阶段
每一个生命周期都是由不同的阶段 (phases) 组成。
例如,default生命周期:
validatecompiletestpackageverifyinstalldeploy
你可以通过mvn + phase命令来调用上述声明流程,运行某个阶段的时候,它之前的所有阶段都会被运行,例如,当你输入mvm package的时候:就会依此从validate阶段运行到package阶段。
clean生命周期:
pre-cleancleanpost-clean
所以我们常用指令:mvn clean package,其步骤,也就不难理解了。
goal
一个阶段会绑定零个或多个goal,如果一个phase没有绑定goal,那么就跳过这个phase。
goal的命名总是:plugin:goal的形式。
一个Phase也会绑定一些默认的goal,例如:
clean生命周期:
| Phase | plugin:goal |
|---|---|
clean | clean:clean |
默认生命周期有很多phase,goal的默认绑定与打包类型相关,下面是打包类型为jar的时候的绑定:
| Phase | plugin:goal |
|---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | jar:jar |
install | install:install |
deploy | deploy:deploy |
没有绑定在特定parse的goal也可以单独执行,例如:
mvn clean dependency:copy-dependencies package
上述命令意味着先执行clean生命周期中的clean及其之前phase,然后执行dependency:copy-dependencies goal,最后执行default生命周期中的package及其之前phase。
Dependency Management
Dependency Management的作用是集中管理依赖,例如:
- excluded部分
- version
Dependency Management中有的信息,dependency中再次使用就不用再写,详见官方文档例子。
相当于依赖的模版,当maven使用某个依赖的时候,会去Dependency Management中寻找有没有对应的模版,如果有,则直接使用其模版,如果模板的内容不合适,可以直接在dependency中指定自己需要的内容,dependency的优先级大于Management。
插件管理(Plugin Managemen)的作用也类似。
Importing Dependencies
由于maven是单继承的,所以在大型项目中,要想引入多份Dependency Management,可以通过导入import scope的依赖来实现。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>maven</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>d</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
此项目就继承了A项目的Dependency Management.
Bill of Materials (BOM) POMs
A BOM is a special kind of POM that is used to control the versions of a project’s dependencies and provide a central place to define and update those versions.
<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>
<parent>
<groupId>com.test</groupId>
<version>1.0.0</version>
<artifactId>bom</artifactId>
</parent>
<groupId>com.test</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>project1</module>
<module>project2</module>
</modules>
</project>
其他想要使用这个库的项目应该把这个POM导入他们的dependencyManagement中。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
When referring to artifacts whose POMs have transitive dependencies, the project needs to specify versions of those artifacts as managed dependencies. Not doing so results in a build failure since the artifact may not have a version specified.
依赖版本的生效顺序:
- 在POM中直接显式指定的版本
- 父项目中提供的版本
- 导入的pom版本
- dependency mediation
如果在继承多个 pom 时发生冲突,则按顺序更早声明的依赖项优先。
Spring BOM
我们可以在我们项目中的dependencyManagement导入spring-framework-bom来确保所有Spring依赖在同一版本。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
这样导入的时候就不需要指定版本属性:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies>
Java打包
Java打包有几种方式
-
Skinny仅打包你所写的部分代码。
-
Thin打包你所写的代码和它的直接一级依赖
-
Hollow与上述
Thin相反 -
Fat/Uber打包所有依赖及依赖的依赖
其中,最实用的就是fat jar,因为其中包含了除了虚拟机外的所有依赖。fat jar也称为Uber jar,其中又分为这几种模式:
-
Unshaded将所有依赖的jar文件中的class文件都平铺到一个jar中,然后使用JVM默认的类加载器加载,对于复杂应用很可能会碰到同名类相互覆盖问题。
代表插件,maven-assembly-plugin.
-
Shaded跟
unsladed类似,但是为了避免依赖冲突,重命名了所有依赖的包名,使用 JVM默认的类加载器。但是改名也会带来其他问题,例如:
-
代码中使用
Class.forName或ClassLoader.loadClass装载的类,Shade Plugin是感知不到的。 -
同名文件覆盖问题也没法杜绝,比如
META-INF/services/javax.script.ScriptEngineFactory不属于类文件,但是被覆盖后会出现问题。
代表插件,maven-shade-plugin.
-
-
JAR of JARs一个Jar包中内嵌了其他Jar包,这个方式彻底解决了解压到出来同名覆盖的问题,但是这个是不被JVM原生支持的,JVM仅会读取包含class文件的Jar包,所以需要自定义ClassLoader。
代表插件:Spring boot plugin.
Repositories标签
连接Maven私服:
<project>
<repositories>
<repository>
<id>my-internal-site</id>
<url>https://myserver/repo</url>
</repository>
</repositories>
...
</project>
如果Maven开启了认证信息,Maven会去Setting文件中寻找ID相同的配置,所以需要如下配置:
<servers>
<server>
<id>my-internal-site</id>
<username>xxx</username>
<password>xxx</password>
</server>
</servers>
注意:
如果pom.xml中配置多个repository,会按照配置的顺序依次查找依赖,找到即停止。
Resources标签
如前文所说,maven约定的资源文件夹为
src/main/resoucessrc/test/resources
这两个目录中的文件也会分别被复制到target/classes和target/test-classes目录中,打包插件也会默认将这两个目录下的文件打包到jar或war中。
这也正是Maven Resources标签所决定的,其默认值为:
<resource>
<!-- 主资源目录 -->
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
当我们想让mapper包下的xml文件也被打包到classpath下的时候,就可以通过修改<resource>的方式来实现。
标签<filtering>是一个bool值,默认值为false。指定打包时的配置文件中是否进行变量替换。
例如:
配置文件中的(默认的占位符为${}):
application.user=${username}
如果<filtering>为true,可以使用如下属性对上述占位符进行替换:
<properties>
<username>mysql</username>
</properties>
References
- 廖雪峰:Maven介绍
- 博客:The Skinny on Fat, Thin, Hollow, and Uber
- Stack Overflow:What is an uber jar?
- 博客:Spring with Maven BOM
- Maven官方文档:Using Mirrors for Repositories
- Maven官方文档:Introduction to the Dependency Mechanism
- 博客:maven中resource配置详解
评论 (0)
登录后即可发表评论
