Maven总结
安装和配置
环境变量
将解压后的bin
目录配置到path
中。主要是mvn
命令,此命令的生命周期(后一个命令的执行将导致其前面的所有命令先执行一遍)如下:
mvn compile
,根据src/main/java
目录编译生成包含字节码的target
目录mvn test
,执行src/test/java
目录下的所有测试用例mvn package
,将此项目打包(jar
或war
)放至target
目录下mvn install
,将此项目打包后的文件放至本地仓库中
mvn clean
则会将本项目的target
目录清楚,有时你拿到的不是一个干净的maven项目(即别人编译、打包过),为了避免目标文件因运行环境不同而导致运行出错,建议先mvn clean
并重新构建一下
配置文件
maven只有一个配置文件,即F:/maven/conf/settings.xml
本地仓库
默认将C盘下用户主目录作为本地仓库的位置,如果你不想占C盘空间,可以设置如下:
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>F:\maven_repository</localRepository>
中心仓库镜像
由于maven的中心仓库部署在国外的服务器上(下载依赖时如果在本地仓库中没有找到该依赖则会去中心仓库),网速不佳,因此可以使用国内的阿里云镜像:
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
|
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
-->
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
</mirrors>
用Maven搭建一个web工程
如下使用IDEA
创建一个带有骨架(在src/java/main/
下添加了一个webapp
用于存放视图文件)的Web工程
然后我们创建Servlet
,发现HttpServelt
报红找不到该类,说明servlet
相关jar
包没有引入,于是在pom.xml
中添加servlet
和jsp
依赖:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
作用域
接着,可以在项目根目录的命令行下键入tomcat:run
运行(maven默认内嵌了一个tomcat6
),你会发现报错:自己写的Servlet
无法转换成HttpServelt
,这是因为tomcat
应用已依赖了servlet-api
和jsp-api
,你在pom
中又引入了一次,导致项目中这两个依赖都有两个jar
包而产生了冲突。
由于我们只需要我们在pom
中引入的servlet-api
和jsp-api
帮助我们度过编译期,而运行期不要将其编入target
使用内嵌tomcat
中的就可以了,因此我们可以将这两个依赖的生命周期(scope
)设置为provide
:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
如此我们写的servlet
和jsp
便可以正常运行了。
如果不写scope
则默认为compile
,即作用域编译期、测试期、运行期;test
则仅作用于测试代码/src/test/java
的编译和运行(典型的如junit
);runtime
表示被依赖项目无需参与编译,但后期的测试和运行需要其参与,如jdbc
驱动
注:如果你使用的JDK是Java8,仍然会抛出异常,因为
tomcat6
不兼容Java8。解决如下
Maven插件
有很多Maven插件可以集成进来帮助我们快速构建应用,比如上述tomcat6
不兼容Java8的问题,我们就可以集成tomcat7
插件:
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8888</port>
</configuration>
</plugin>
</plugins>
</build>
如此,键入tomcat7:run
命令就能使该项目运行在端口为8888
的tomcat7
上,而键入tomcat:run
命令则仍将此项目运行在默认8080
端口的tomcat6
上。
依赖冲突
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
当你引入上述依赖时,由于spring-context
自身依赖5.0.2.RELEASE
版本的core、beans、expression、aop
等依赖,又由于maven的传递性,当前项目也会引入这些依赖。此时,context
称为直接依赖,后者称为传递依赖。
如果此时你再手动引入一个4.2.4.RELEASE
版本的beans
依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
因为context
和beans
都依赖core
,那么Maven间接依赖进来的core
取哪个版本呢?
原则一:先进先留原则
如果两个直接依赖引入了两个不同版本的相同依赖,在
pom
中书写在前的将被保留。
也即,如果书写如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
那么引入的将是5.0.2.RELEASE
版本的spring-core
,否则将两者的书写顺序颠倒:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
引入的spring-core
就是4.2.4.RELEASE
版本的了。
原则二:依赖链短者保留
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
如上,无论spring-beans
和spring-context
的书写顺序如何,引入的spring-core
均以第15
行的5.0.2.RELEASE
为准,因为通过依赖传递形成的依赖链最短。
原则三:使用exclusion排除依赖【推荐】
当发生依赖冲突时,我们可以将所有不想要的依赖通过exclusion
显式声明的方式排除掉,这样最为直观。如,排除掉4.2.4
版本的spring-core
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>org.springframework</artifactId>
<groupId>spring-core</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
其他标签
dependencyManagement
dependencyManager
用来统一管理版本号,让子项目中引用一个依赖而不用显示的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement
元素的项目,然后它就会使用在这个dependencyManagement
元素中指定的版本号。这样,一旦整个系统需要迭代升级,只需将定义在父项目的dependencyManagement
中的版本号更换即可实现其所有子项目的依赖升级。
properties
定义一些键值对,已达到复用、减少修改的目的。
父子工程
父工程通常不存放源代码,仅仅起到一个聚合的作用,将相关联的一些模块聚合在一起。需要注意一下几点:
-
父工程的
pom
中的package
必须是pom
-
父工程通常通过定义
dependencyManagement
来控制子模块的依赖的版本号统一 -
父工程可在
dependency
中定义各子模块都可能用到的依赖,这些依赖会自动被子模块继承,也即子模块可省略在其pom
中对这些依赖的引入 -
各子模块虽然继承同一父工程,但彼此之间没有任何直接关联关系,如果模块A需要用到模块B的功能,则需现将模块B
mvn install
到本地仓库,然后在模块A的pom
中引入模块B的坐标。注:对于任何maven工程,无论是父工程还是子模块,要想其编译成功,其所有依赖必须要能在本地仓库或远程仓库中找得到
创建子模块的方法:右键父工程,选择new module
。子模块可以在父工程的目录下,也可以和父工程同一目录,这没有任何影响。是否是父子工程需要看父工程pom
中的module
定义和子模块pom
中的parent
定义,子模块的parent
中写父工程的groupId、artifactId、version
,自己的groupId
和version
都将继承父工程的,只需自定义artifactId
。