Web | Maven
仅针对整体说明了,对于众多软件通性操作只是一带而过,可以和其它写的更基础详细的博客一起看,加深理解。
1. maven是什么?
maven是一个由java写的(必不可少依赖jdk)项目管理工具,软件解压即可。提供有项目的管理命令,如编译、打包等。
2. maven可以做什么?
它规定了开发平台中项目开发的目录结构,以便方便管理。它可以管理插件(如tomcat插件)和jar包、并管理项目的编译、测试、运行、打包、安装 ,部署等(maven管这些过程叫构建)。如:你在项目文件夹中执行maven提供的cmd命令:mvn tomcat:run,就可以进行编译,运行打包,部署了。当然和开发工具集成后只需要一个按钮即可。
jar包管理:通过pom.xml文件中的坐标到仓库找对应的jar包。
3. 如何安装?
下载 -- 解压 -- 配置环境变量,当然一如既往软性规定无中文无空格的目录。
当然一般不会单独使用,一般集成在开发工具中使用,如IDEA。
关于maven环境变量网上应该一大堆,这里只强调一点,环境变量名可以是MAVEN_HOME (和jdk很像)、M2_HOME,最好是后者,据说兼容性好一点。
注意:3.3+版本需要jdkj.7+以上的支持 ,不过基本上jdk8用的比较多,jdk12都出来了
4. maven目录结构
bin:存放了maven的命令,配置path环境变量,以便能在cmd里面任意调用mvn命令,当然有些命令是必须在相应文件中才有效的,比如打war包命令。
boot: 存放了一些maven本身的引导程序(调用lib目录下的jar的java 加载框架),如类加载器等
conf:存放了maven的一些配置文件,如setting.xml文件
lib:存放了maven本身运行所需的一些jar包
注意:再次强调maven由java编写,因此依赖于jdk,所以需要安装并配置好JDK环境变量。
5. Maven仓库
本地仓库:就是本地存储jar包的一个文件夹。用来存储从远程仓库或中央仓库下载的插件和jar包 ,可以理解为远程仓库的缓存。项目使用一些插件或jar包时,优先从本地仓库查找。因此随着maven的不断的使用下载构件,它的本地仓库将越来越大。默认本地仓库位置在 ${user.dir}/.m2/repository,${user.dir}表示windows用户目录,可以在Setting.xml中修改为自定义目录。
中央仓库 :在maven软件中内置一个远程仓库地址http://repo1.maven.org/maven2 ,它是中央仓库,服务于整个互联网,它是由Maven团队自己维护,里面存储了非常全的jar包,它包含了世界上大部分流行的开源项目构件。 但由于服务器在国外,下载很慢,所以一般会在配置文件中指定阿里的镜像代理,当然还有一些其它的代理和库,网上很多。
私服:一般公司内部会建一个仓库,公司内部局域网内使用。
setting.xml文件配置:maven仓库地址、私服等配置信息需要在setting.xml文件中配置,分为全局配置和用户配置。 在maven安装目录下的有 conf/setting.xml文件,此setting.xml文件用于maven的所有project项目,它作为maven的全局配置。 如需要个性配置则需要在用户配置中设置,用户配置的setting.xml文件默认的位置在:${user.dir} /.m2/settings.xml目录中,${user.dir} 指windows 中的用户目录。
maven会先找用户配置,如果找到则以用户配置文件为准,否则使用全局配置文件。
6. maven开发工具中的目录结构
maven规定了一套开发时的目录结构(下图是别人的图)以方便maven工具去管理,当然打包时会按照web工程目录结构打包,两者目录对照关系我之前文章中有一个图很清晰的说明了。
7. maven中的基本命令
这个也体现了maven的功能,当然集成到开发工具中只是一个按钮的事情。主要有clean、compile、test、package、install、deploy
8. maven的生命周期
这个了解,不过可以借助这个很好的罗列一下maven的命令,因为每个生命节点对应一个基本的执行命令。
分为了三套生命周期,Clean Lifecycle 、Default Lifecycle、
Site Lifecycle ,有兴趣可以扩展了解。
注意后面的执行是依赖前面执行的(test可以设置排除),所以后面的执行,前面必然会执行。
9. pom.xml文件
打包类型:
jar:执行package会打成jar包
war:执行package会打成war包
pom:用于maven工程的继承,通常父工程设置为pom
10. maven功能总结
说到这里不难理解,maven的核心功能有两大类:一键构建 & jar包管理
一键构建:项目的编译、测试、打包等到最终形成一个符合web项目的目录结构,这些流程原来我们可能需要自己去复制、编译、创建目录等,且容易出错。交给maven只是一个命令的事情,在开发工具中只是一个单击动作。
另外可以了解一下的是管理项目生命周期执行过程都是基于底层插件完成的,从下图IDEA集成后目录可以看出。
jar包管理:自不用说,再也不用我们复制粘贴jar包了,开发时项目体积减小(当然打war包时,会将jar包文件打包到lib目录下的),而且jar包形成了一套体系,可以避免部分jar包冲突的问题。
11. IDEA集成Maven
个人建议:不要使用maven骨架创建maven项目,感觉会很乱。
集成和创建maven工程过程如下:
集成
创建
如上,往pom.xml文件里扔坐标就可以了
12. 其它说明
12.1 jar包下载的很慢?换镜像代理
12.2 如何知道坐标?
一些网站上有提供对应的坐标:
http://search.maven.org/
http://mvnrepository.com/
12.3 开发目录
如果你觉得目录划分是软性规定那就错了,每个文件夹都设置了相应得属性,像在java文件夹中就没法建html文件,当然你也可以在项目结构中更改文件夹为web资源目录,也可以右键更改其它类型。
13. 关于插件(tomcat为例)
前面也说了,maven命令底层都是由相应得插件实现的,除了一些基本生命周期命令相关的插件外,maven自带tomcat插件(6版本的,不知道会不会更新,好像不太好用)。前面说的进入maven工程目录(当前目录有 pom.xml 文件),运行 `tomcat:run` 命令就会完成项目部署,项目部署当然离不开服务器软件。这里使用的是maven内部自带的tomcat插件。
在IDEA中的操作:
上面是一个指定目录,输入命令执行的通用操作,在第二个输入框输入`tomcat:run` 和上面的执行效果是一样的,默认端口号是8080;
为什么这么操作,不直接点击命令?因为默认显示的插件每显示tomcat呀,下面的图也可以看出没有tomcat,那个tomcat7是我在pom.xml文件中加入的。
当然你也可以引入其它版本的tomcat插件如下,这里可以指定端口和虚拟路径:
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId> <!-- 看这个tomcat7在前 maven在后,以及groupId的值,估计是apache另外给maven开发的一个插件(猜的) -->
<configuration>
<port>80</port> <!-- 指定端口 -->
<path>/</path> <!-- 请求路径(虚拟路径:项目文件夹位置的映射)-->
</configuration>
</plugin>
</plugins>
</build>
在右边目录中也可以看到加入了tomcat7插件
另外如何定义一个没显示的maven命令呢?
另外,除了使用maven自带的tomcat插件或导入的其它版本tomcat插件,也可以使用本地的tomcat集成到IDEA工具进行相应的编译到部署的操作,具体配置过程网上比较多。
IDEA集成本地tomcat
14. 依赖jar包的作用(依赖)范围
以jar包重复冲突来阐述这个问题:
比如你导入了:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>compile</scope>
</dependency>
但是这个jar包tomcat里面也有,所以当你部署完,运行时会出错,但如果你不导入,编译都不通过。这个时候你只需要将jar包下面加一个作用范围,指定其在编译时起作用就可以了。
其它依赖范围如下:
依赖范围 | 编译 | 测试 | 运行 | 说明 |
---|---|---|---|---|
compile | √ | √ | √ | 编译范围,此范围为默认依赖范围。编译范围的依赖会用在编译、测试、运行,由于运行时需要所以编译范围的依赖会被打包。 |
test | × | √ | × | test范围依赖 在编译和运行时都不需要的jar包,它们只有在测试编译和测试运行阶段可用,比如:junit。由于运行时不需要所以test范围依赖不会被打包,其实用compile也无所谓。 |
provided | √ | √ | × | provided依赖在编译和测试时需要,在运行时不需要,意味着其根本就不会打包到lib目录中。比如:servlet api被tomcat容器提供。 servlet-api 、jsp-api ------- provided (编译、测试有效, 运行时无效防止和tomcat下jar冲突,如果导入的版本和tomcat自带的版本不一致还会产生各种各样的其它问题,所以这两个一定要注明provide范围) |
runtime | × | √ | √ | runtime依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如:jdbc的驱动包。由于运行时需要所以runtime范围的依赖会被打包。 |
system | √ | √ | × | system范围依赖与provided类似,但是你必须显式的提供一个对于本地系统中JAR文件的路径,需要指定systemPath磁盘路径,system依赖不推荐使用。 |
15. 导入工程
这里想说的是maven识别maven工程的依据是是否有pom.xml文件。当你对一个没有pom.xml的文件夹使用maven打包时会报错,有兴趣可以用在cmd窗口用maven命令试一下。
另外IDEA也会有识别的标准,但好像都可以导入,然后会自动生成一些相关文件和文件夹,这一点和Eclipse有点不同,eclipse好像不会识别这些目录。
16. 依赖的传递性
这里将理解为什么说maven可以解决部分jar冲突的问题,首先理解什么叫传递依赖:
导入如下jar包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
会发现出现除了spring-webmvc(直接依赖)以外的其他jar包。因为我们的项目依赖spring-webmv.jar,而spring-webmv.jar会依赖spring-beans.jar(间接依赖)等等,所以spring-beans.jar这些jar包也出现在了我们的maven工程中,这种现象我们称为依赖的传递性。
再如你有一个模块写的公共类或工具类,你在另一个模块中引入,那么前一个模块依赖的jar包也会导入。
父模块的依赖jar包,也会导入子模块中,所以父模块通常用作导入公共jar包。
这里还需要了解两个概念直接依赖和间接依赖,我想根据前面的介绍猜的出来啥意思。
注意:非compile范围的依赖不会传递,但是父模块仍然可以传递过来。
17. maven调优原则
可以考虑,如果导入的两个包A、B都依赖C包,但两个C的版本不同,这个时候会将两个C包同时引入吗?当然不会,maven有自己的原则。
-
第一声明者优先原则
在pom文件定义依赖,先声明(坐标写在前面的)的依赖为准 -
路径近者优先原则
这种情况对于本项目如果同时直接导入A、B、C那么这里将以这个直接导入的C为准,都是间接依赖的话不存在谁远谁近,只有直接依赖比间接依赖近。
依赖排除:在坐标中加入如下的标签就可排除此jar包指定的依赖的jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.4.RELEASE</version>
<exclusions>
<exclusion> <!-- 排除掉spring-webmvc依赖的spring-beans包 -->
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
18. jar包冲突
由上面的原则,不难看出虽然上面可以解决依赖冲突 ,但是容易出现C的版本不适合A/B而出现版本冲突问题 这个暂时没法直接解决方法,只能人为的留意,下面要说的jar包版本管理则可以某种层面上辅助解决这个问题,在实际企业开发中会有架构师专门来编写pom.xml。
19. 依赖版本管理
<!--统一命名版本,方便更改-->
<properties>
<junit.version>4.12</junit.version>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<!-- 统一管理版本号,后续的依赖坐标无需指定版本号,以避免版本冲突,当然前提是这里得指定好 。主要是为了对版本号的一个集中管理。一般写在父模块里-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version> <!-- EL 表达式-->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependencies>
<dependencyManagement>
20. 依赖范围对传递依赖的影响
前面谈到过依赖范围和传递依赖,但依赖范围对传递依赖有一定的影响,当然个人测验出对于父子模块不适用,父模块到子模块的继承完全是相当于将父模块pom.xml文件内容复制粘贴到子模块。
21. 继承与聚合
继承:
继承是为了消除重复,如果将dao、service、web分开创建独立的工程则每个工程的pom.xml文件中的内容存在重复,比如:设置编译版本、锁定spring的版本的等,可以将这些重复的配置提取出来在父工程的pom.xml中定义。
注意:一般父模块打包方式为pom
聚合:
在父模块中加入如下的模块,通常写在父模块信息标签的下面,但也并非是要求父子关系,任何多个模块都可以聚合,另外模块一定要是pom打包方式,这个时候对父模块打包就可以将其它子模块也打包。
另外如果不加这个单独打子包,则需要先打父包。
<modules>
<module>company-agent</module> <!-- 指定其它聚合的模块,写相对路径-->
<module>company-java-core</module>
<module>company-https</module>
<module>company-http</module>
</modules>
注意 :以上说的模块、工程都是一个意思,都指的是一个项目。这是只是在IDEA中和Eclipse中叫法不同而已。
打包是交给IDEA的,部署一个项目试试,看是否放在了本地tomcat对应目录!
tomcat集成到IDEA也会将两个基本的包给导入进来,如下