引言
很多公司里的web项目是都是用maven管理,maven可以非常方便的管理项目的各种依赖,确实很好用,但是对于刚开始使用的初学者,网上的很多教程基本上跟着操作两步就会出问题,可能是因为细节没有说清楚或者版本的问题,给学习带来很多困难,于是根据我这几天的尝试和学习,想自己写了一篇教程,让初学者入门时少走弯路,本文中maven的默认版本是3.1.1,操作系统是windows,本文中只介绍了maven对于普通程序员来说比较重要和常用的部分,至于其他很多东西像nexus服务器的配置等,我也不是很懂,因此本文中就没有写,所以并不适合有经验的程序员阅读
使用maven前的准备
1.配置环境变量
首先要配置java环境变量,这个就不多说了,然后要配置maven的环境变量,本文中将maven解压后放在了如图所示的路径下:
然后和配置java环境变量一样,然后在系统变量中添加M2_HOME变量,值为
D:\Program Files\Java\apache-maven-3.1.1
然后在path变量的末尾添加
%M2_HOME%\bin;
然后打开cmd,输入
mvn -version
,打印出如图所示内容则配置成功2.修改settings.xml
打开maven所在目录进入conf文件夹,会看到里面有一个settings.xml文件,这个是maven的全局配置文件,打开后找到<mirrors>
,在<mirrors></mirrors>
中添加如下内容:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
如图所示
因为maven的所有功能都是基于插件的,当需要用到某一个插件时就会自动到服务器去下载,包括maven项目需要的各种依赖包,也都是去服务器下载的,中央服务器上保存了几乎所有常用的依赖包和插件,但是在国内因为墙的原因,每当maven需要从远程仓库下载插件或者依赖包时都会速度非常缓慢甚至下载失败,这一步其实就是给maven配置了一个maven远程仓库的镜像,阿里云的镜像亲测速度挺快,所以配置了这个,网上还有人说开源中国也有一个maven的国内镜像,后来好像因为什么原因关闭,我试过了没有用,所以最终选择阿里云的镜像。
如果公司里公司里有自己的仓库(一般是nexus服务器)的话,最好直接找同事要一份setting.xml然后覆盖掉
C:\Users\Administrator(当前使用电脑的账户名)\.m2\settings.xml
,因为里面可能还配置了公司自己的nexus服务器,服务器里可能存储了一些公司自己开发的依赖包,这些依赖包是不可能从中央仓库或其国内镜像中下载到的。当maven从网络上下载下来依赖包之后会放在本地仓库中,本地仓库的默认地址为
C:\Users\Administrator(当前使用电脑的账户名)\.m2\repository
如果不想放在这里也可以在settings.xml的根标签中配置
<localRepository>目录</localRepository>
这样下载的依赖包和插件就会放在配置的目录中了
本文使用maven的默认地址
创建一个简单的maven项目
在D盘下新建一个maven_test文件夹用于测试学习
进入cmd,使用cd命令进入目录D:\maven_test
,即输入命令:
cd /d D:\maven_test
,然后输入命令:
mvn archetype:generate
第一次输入这个命令,会先去仓库下载一些东西到本地,然后会让你选择原型,此时选择7,即
maven-archetype-quickstart
之后会让你依次输入groupId,artifactId,version,package先按照如下图所示输入,以后再解释其含义:
最后再确认一下输入的这些属性,一个最简单的maven项目就新建好了
访问目录
D:\maven_test
发现出现了一个firstmaven文件夹
注:有的博客中会告诉你用mvn archetype:create
创建一个最简单的maven项目,但是尝试了之后会发现,在高版本的maven中会出现ERROR,网上搜索发现有的博文说在maven3.0.5以上的版中舍弃了用create创建项目
maven项目结构
一个maven项目由groupId,artifactId和version(简称GAV)唯一确定,当别人的项目里需要引入你写的包作为依赖时,你只需要将这三个属性的值告诉他就可以了,有的博客里将其成为maven项目的坐标,我觉得很形象,就沿用了这一叫法。
点开刚刚新出现的firstmaven
文件夹,可以点开看看里面的目录层次,所有maven项目的目录结构都是统一的,如下图是标准的maven目录结构:
├───src
│ ├───main
│ │ └───java
│ │ └───resources
│ │
│ │
│ └───test
│ └───java
│
└───target
其中,src/main/resources约定用于存放xml等配置文件,src/main/java约定用于存放源代码,src/main/test用于存放单元测试代码,src/target用于存放编译、打包后的输出文件,刚刚创建的这个项目缺少一个resources文件夹,需要我们自行创建一下,除此之外还缺少一个target目录,这个不用担心,在使用maven进行编译打包时,target会被自动创建的。
在项目目录下还会看到一个pom.xml文件,pom是Project Object Model(项目对象模型)的简称,通过pom.xml我们可以给项目进行一些配置。
常用的pom标签
点开pom.xml文件,发现根元素是<project></project>
,groupId,artifactId和version是刚刚我们创建项目时填入的项目的坐标;
url是指开发这个项目的组织的官方网站,目前没有什么用;
<packaging>jar</packaging>
表示项目将会被打包成jar文件,除了jar,还可以配置成war或者pom,war表示将会打包成war文件,pom表示这是一个多模块项目的父项目,讲到多模块项目时会再具体说明;
比较重要的是<dependencies></dependencies>
,项目的依赖包全都是在这里配置的,项目默认配置了对junit的依赖,如果还需要其他依赖的话请浏览http://mvnrepository.com
,查找需要的依赖,点进去,网页会提供如下xml代码(以Spring为例):
将该代码添加到
<dependencies></dependencies>
当中即可完成依赖添加,很明显比以前手动去网上下载然后添加依赖的方式要方便许多.除此之外还要很多pom标签,我们等接下来用到了再进行讲解
maven创建web项目
接下来看看如何用maven创建javaweb项目,打开命令行进入D:\maven_test
,运行命令
mvn archetype:generate
然后会提示让你输入要选择的原型,这里输入10,即webapp类型,如图:
然后的步骤就和简单maven项目的创建一样了,这里我是这么命名的:
然后打开目录
D:\maven_test
发现多了一个webapp文件夹,打开文件夹看一下目录结构,标准的maven web项目的目录结构如下:
├───src
│ ├───main
│ │ └───java
│ │ └───resources
│ │ └───webapp
│ │
│ └───test
│ └───java
│
└───target
自动生成的项目在main目录下还缺少一个java目录,我们新建一下。
不要着急将这个项目导入IDE进行编码,要先对这个项目进行一些改造,否则你在编码时将会面临一些困扰,下面我来说一下这个自动生成项目存在的问题:
1.该项目不支持el表达式,所有的el表达式都会在页面上直接输出;
2.如果将项目导入IDE中,编写jsp或者servlet代码时没有任何提示;
如果有兴趣的话可以亲自去试一试,总之我在初学maven时感到很困扰,经过尝试,按如下步骤可以把改项目改造成正常的web项目:
1.打开该项目的web.xml文件,会发现如下内容:
这似乎是Servlet2.x版本的web.xml,那个时候还没有el表达式,所以页面中的el表达式当然不会被解析,我们将其内容全部删除,换成如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">
</web-app>
这个是支持el表达式的新版web.xml,如果有兴趣尝试一下的话,会发现el表达式已经可以被正确解析了
2.打开pom.xml,查看标签<dependencies></dependencies>
,发现虽然是web项目,但是maven引入的依赖仍然只有一个junit,没有对于jsp和servlet的依赖,所以在IDE中编写jsp或servlet时会没有代码提示
去http://mvnrepository.com
搜索依赖包,复制粘贴代码到<dependencies></dependencies>
中,然后稍作修改,例子如下:
<!-- jsp API -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<!-- Servlet API -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
这里的dependency和之前不太一样,要多一个<scope>provided</scope>
,这个用来配置依赖的“范围”,provided表示依赖仅仅在编译范围起作用,打包或者运行时并不会被包含进去,之所以这么配置是因为servlet和jsp的jar包在javaweb容器中已经包含,并不需要被打包进去,仅仅是在编译时需要,其他常用的值包括:compile,runtime,test,system。compile是默认值,如果不写scope标签时就隐含是这个,编译,运行打包时都会包含,test表示仅仅在测试阶段包含,其他的并不是很常用,我也没有用过,就不多说了。
在做完这些之后,以后项目如果被导入IDE,在写JSP或者Servlet时就会有提示了。
maven自定义原型
在发现maven自带的webapp原型很坑之后,我们按照如上方法进行了改造,但是每次这么改造还是挺繁琐的,所以我们决定将自己改造过的webapp项目做成一个自己的原型,按照下面的步骤即可定义出自己的原型:
1.先打开cmd,进入到项目目录下,即D:\maven_test\webapp
2.执行命令:
mvn archetype:create-from-project
3.切换目录至D:\maven_test\webapp\target\generated-sources\archetype
,即输入命令:
cd target/generated-sources/archetype
4.输入命令:
mvn install
然后就结束了,就是这么简单,进入项目目录看一下,发现多了一个target文件夹,之前说过输出文件都是输出到target文件夹下的,target是maven项目的一个标准文件夹,进去看一下,一直进入到目录
D:\maven_test\webapp\target\generated-sources\archetype
中,发现这里也有一个pom.xml文件夹,打开这个文件看一下,下面这三个值要记住了,它们就是自定义原型的坐标,接下来要用到
再次进入cmd,进入目录
D:\maven_test
,然后输入命令,切记-D后面不要加空格
mvn archetype:generate -DarchetypeGroupId=org.test.webapp -DarchetypeArtifactId=webapp-archetype -DarchetypeVersion=1.0
然后就会发现生成了一个符合刚才简模板的项目,其中的参数-DarchetypeGroupId,-DarchetypeArtifactId和-DarchetypeVersion的值就是刚才看到的项目原型的坐标,也可以不在命令行中输入原型坐标,直接输入mvn archetype:generate
然后你在选择原型是会看到多出来了一个刚刚创建的web-archetype原型
Settings.xml
maven安装好后会在两个地方有settings.xml,一个是刚刚在配置仓库镜像时接触过的在maven目录下的conf中的settings.xml,这个是maven的全局配置文件,对本台计算机的所以用户有效;还有一个是在C:\Users\Administrator(当前使用电脑的账户名)\.m2\
目录下的settings.xml,它只对当前使用计算机的用户有效,两个settings.xml中可以配置的标签是相同的,对于普通程序员来说,我们只需要用到少量的几个标签,比如之前说过的<mirrors></mirrors>
,所以本节就不单独讲解settings.xml的标签了,等到接下来的章节用到时再讲。
maven项目的生命周期
我对生命周期的理解也不是很深入,就说一些工作中经常用到的吧。
maven生命周期有很多阶段,大家可以自行去网上搜索相关资料,但是目前我在工作中只用过clean,install和deploy。
生命周期是专业术语的叫法,我更喜欢将其理解成maven的功能或者服务,当你在pom.xml所在目录执行类似与mvn clean
, mvn install
和mvn deploy
生命周期命令时,命令就会完成相应的功能,下面我来简单说一说这三个命令的功能
mvn clean
会清除target目录及其所有内容
mvn install
会将项目发布到本地代码仓库
mvn deploy
会将项目发布到所配置的远程仓库当中,公司的项目一般是发布到公司自己的nexus服务器当中,此时其他开发者就可以在引入你写的项目进行依赖
配置deploy发布的服务器位置
maven需要知道公司服务器的位置才能够在deploy时将项目发布到服务器中,所以在deploy之前还需要在setting.xml和项目的pom中进行一下配置,不过如果你的settings.xml是找同事要的,项目又是从svn上拷下来的,基本上都已经配置好,所以对于普通程序员来说,基本上不需要自己配置,只做了解即可,配置步骤如下:
1.setting.xml中的配置:
在settings.xml中配置nexus服务器,一般会配置两个,一个是用来存放项目的发布版本,一个用来存放快照(snapshot),发布版本一般是已经稳定了版本,而快照一般是临时存储用的.
在settings.xml的根标签下添加<servers></servers>
, 如下:
<servers>
<server>
<id>nexus-releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
id可以任意填写,但是最好能够像如上代码那样标识出发布版本的服务器和快照服务器,<username>
和<password>
分别是该nexus服务器的用户名和密码
-
项目pom中的配置:
在根标签下加入如下的<distributionManagement></distributionManagement>
:
<distributionManagement>
<repository>
<id>nexus-releases</id>
<url>http://192.168.1.250:8084/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<url>http://192.168.1.250:8084/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
其中配置了一个repository和snapshotRepository,其中repository是用于存储发布版本,snapshotRepository用于存储快照,所谓快照,所有version以"-SNAPSHOT"结尾的项目发布时都会被maven识别快照并发布到snapshotRepository.
其中的id标签就是你之前在settings.xml中配置的server的id,它表示你选择了哪一个server.
url标签很显然就是仓库的地址,上面只是举个例子,复制粘贴的时候记得改成自己的nexus服务器的地址.
这两步便已完成了配置,当你deploy项目时,项目就会出现在你指定的nexus服务器上。
maven多模块项目创建
maven多模块项目是用maven进行项目管理的最佳实践之一,一般在javaweb项目中会将每一层(domain,dao,service,web)作为一个单独的模块,有时候一些通用的工具类可能会再形成一个模块(util)。
下面开始动手操作:
打开命令行,先进入我们的实验目录 D:\maven_test
按如下步骤操作:
- 输入命令:
mvn archetype:generate -DgroupId=org.test.multimodule -DartifactId=multimodule -Dversion=1.0-SNAPSHOT
之前我们都是直接用mvn archetype:generate
创建项目的,直接用不带参数的该命令时,会在创建项目时提示你输入GAV(groupId, artifactId, version),在这里我们直接使用命令参数-DgroupId, -DartifactId, -Dversion指定GAV,在之后创建模块时将会看见这么做的好处。选择原型时直接按回车,选择默认的7号原型(quick-start)
- 打开刚刚创建的文件夹multimodule,删除src目录,在多模块项目的父项目中是不放资源文件的,之后打开pom.xml,找到
<packaging></packaging>
标签,发现标签内写的是jar,之前在讲pom标签时提到过这个标签,jar说明项目在打包阶段会被打包成jar,我们将其改为pom,如下:
<packaging>pom</packaging>
,说明它是多模块项目的父项目,如果不修改的话创建模块时将会出错。
然后找到<dependencies></dependencies>
标签,将这个标签删掉,父项目不需要依赖任何东西。 - 在命令行中使用cd命令进入刚刚multimodule目录,将刚刚创建multimodule项目的命令复制一份,仅仅修改-DartifactId参数,得到如下几个命令:
mvn archetype:generate -DgroupId=org.test.multimodule -DartifactId=multimodule-domain -Dversion=1.0-SNAPSHOT
mvn archetype:generate -DgroupId=org.test.multimodule -DartifactId=multimodule-dao -Dversion=1.0-SNAPSHOT
mvn archetype:generate -DgroupId=org.test.multimodule -DartifactId=multimodule-service -Dversion=1.0-SNAPSHOT
mvn archetype:generate -DgroupId=org.test.multimodule -DartifactId=multimodule-web -Dversion=1.0-SNAPSHOT
从-Dartifact参数中就可以看出这些模块分别对应的是domain, dao, service以及web层,至于原型的选择,domain, dao, service只要选择默认的quick-start原型即可,web层则可以用我们之前创建的web-archetype原型。
除此之外,还要打开各个模块的pom.xml把根标签下直接子标签<groupId></groupId>
, <version></version>
,应为这两个属性已经直接通过上面的<parent></parent>
标签从父项目中继承,这里不需要重复。
到此,多模块项目的创建就完成了,我们来看一看项目发生了哪些变化。
首先打开父项目(即D:\maven_test\multimodule
目录)的pom.xml,会发现多了如下的<modules></modules>
标签:
这个表示了这个项目的各个模块,每个
<module></module>
标签中写的是模块的artifactId然后随意打开一个模块的pom.xml瞧瞧,就以mutimodule-domain模块为例吧,打开它(即
D:\maven_test\multimodule\multimodule-domain
目录下的)的pom.xml,发现它的GAV和一般的项目不太一样,如下:它通过
<parent></parent>
标签继承了父项目的GAV,也可以通过在pom.xml根标签下定义GAV来覆盖掉它,这里模块的<artifactId></artifactId>
就把<parent></parent>
中的artifactId给覆盖掉了。
maven与IDE结合
maven可以和例如eclipse, IntelliJ等IDE进行整合,IDE会给我们提供更为方便的操作,不过这里还是有个建议,最好先用maven的指令创建好maven项目,然后将其导入IDE,这样做的原因一个是速度比IDE快,而且在多模块项目项目中直接复制粘贴修改maven指令也比操作图形界面要方便多了,对与一些IDE来说还有其他好处,以我最常用的IntelliJ为例,从外部导入maven的web项目会自动生成war和war exploded,而用IDE生成的maven的web项目不会自动生成这两样东西(注:war和war exploded的区别是:如果改变了资源文件需要重新打包才能改变war,而war exploded不用,方便进行测试),maven和eclipse整合网上资料很多而且我也很久没有用eclipse了,所以这里就不讲了,这里就讲一下maven和IntelliJ的整合,用eclipse的朋友可以直接跳过本节直接看下一节了
maven和IntelliJ的整合
IntelliJ自带有maven,不过自带的没有命令行,对于我这种更喜欢用命令行创建maven项目的人来说,一般不喜欢用IntelliJ自带的,而是配置成自己安装的maven,方法如下:
菜单上找到 File -> Settings ,打开Settings后在弹出框的左上角搜索maven,搜索到后点击并进入配置,如图:
上图是最重要的三个配置,1处配置的是你所安装的maven的根目录,2处和3处建议就按照如图所示进行配置.
顺便提醒一下,如果使用IntelliJ自带的maven的话,默认的settings.xml和本地仓库就不是在上面说的目录中了,而是在IntelliJ安装目录所在盘符下一个隐藏的文件夹之中.
maven使用中的常见问题
这里就先讲一个我经常遇到的问题,在maven多模块项目中,内部模块之间常常存在一些互相依赖,假设模块B依赖模块A,我修改了A,增加了一些功能,然后从B中调用这些功能却调用不了,这是什么原因呢?原来是我修改完A之后并没有将其install或者deploy,所以B依赖的还是修改之前的A,然后B就无法体现A中功能的变更,综上我们在开发maven项目时要养成一个好习惯,修改完一个模块之后立即将其install或者deploy,然后再去搞下一个模块。