Web基础之Maven
Web基础之Maven
Maven是一个优秀的项目管理工具,可以很轻松的管理项目。
POM和LifeCycle
POM:Project Object Model。也就是项目模型,简单来说就是对项目进行建模,既然是建模,那就会有一些属性来定义这个项目。而配置文件pom.xml就是以xml形式描述这个建模,所以里面会有一些项目特定的属性,比如:groupId、artifactId、version……
那么Lifecycle又是什么呢?翻译过来就是生命周期,在项目构建过程中有着不同的阶段(phase),总共有多少个阶段呢?
一个标准的Lifecycle有着以下几个阶段:
validate: 用于验证项目的有效性和其项目所需要的内容是否具备
initialize:初始化操作,比如创建一些构建所需要的目录等。
generate-sources:用于生成一些源代码,这些源代码在compile phase中需要使用到
process-sources:对源代码进行一些操作,例如过滤一些源代码
generate-resources:生成资源文件(这些文件将被包含在最后的输入文件中)
process-resources:对资源文件进行处理
compile:对源代码进行编译
process-classes:对编译生成的文件进行处理
generate-test-sources:生成测试用的源代码
process-test-sources:对生成的测试源代码进行处理
generate-test-resources:生成测试用的资源文件
process-test-resources:对测试用的资源文件进行处理
test-compile:对测试用的源代码进行编译
process-test-classes:对测试源代码编译后的文件进行处理
test:进行单元测试
prepare-package:打包前置操作
package:打包
pre-integration-test:集成测试前置操作
integration-test:集成测试
post-integration-test:集成测试后置操作
install:将打包产物安装到本地maven仓库
deploy:将打包产物安装到远程仓库
在执行任意一个阶段时,之前的每个阶段都会执行。
注意,以上的每个阶段只是一个约定,并没有说每个阶段具体干什么。这有点类似接口,约定了一个项目需要有这些过程,但是每个过程具体干什么需要由我们自己去指定。这里再引入一个概念MOJO。什么是MOJO,官方解释是可执行的goal(目标),下面是stackoverflow上的高票答案:
What is a Mojo? A mojo is a Maven plain Old Java Object. Each mojo is an executable goal in Maven, and a plugin is a distribution of one or more related mojos.
In short, a mojo is a maven goal, to extend functionality not already found in maven.
翻译一下就是MOJO就是一个可执行对象(goal,目标),这个对象可以获得已经在maven里面定义过的功能。而我们需要做的就是把这个可执行对象和具体的阶段绑定起来。
在哪里绑定呢?在pom.xml里面的<build>
属性里,因此一个plugins
如下:
<!--使用的插件列表 。 -->
<plugins>
<plugin>
<groupId></groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<!--在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -->
<executions>
<execution>
<!--执行目标的标识符,用于标识构建过程中的目标,或者匹配继承过程中需要合并的执行目标 -->
<id>assembly</id>
<!--绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 -->
<phase>package</phase>
<!--配置的执行目标 -->
<goals>
<goal>single</goal>
</goals>
<!--配置是否被传播到子POM -->
<inherited>false</inherited>
</execution>
</executions>
<!--作为DOM对象的配置,配置项因插件而异 -->
<configuration>
<finalName>${finalName}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptor>assembly.xml</descriptor>
</configuration>
<!--是否从该插件下载Maven扩展(例如打包和类型处理器), -->
<!--由于性能原因,只有在真需要下载时,该元素才被设置成true。 -->
<extensions>false</extensions>
<!--项目引入插件所需要的额外依赖 -->
<dependencies>
<dependency>...</dependency>
</dependencies>
<!--任何配置是否被传播到子项目 -->
<inherited>true</inherited>
</plugin>
</plugins>
上例就将single
这个goal绑定到了package
这个phase。
这样虽然指定了要做什么,但是并没有在哪做、用什么做。这里需要说一下Maven的理念就是convention over configuration(约定优于配置)。这一点和ant有非常大的区别,例如使用ant来进行编译时,我们需要指定源文件的位置,输出文件的位置,javac的位置,classpath... ...在maven中这些都是不需要,若没有手动配置,maven默认从<项目根目录>/src/main/java这个目录去查找Java源文件,编译后的class文件会保存在<项目根目录>/target/classes目录。在maven中,所有的PO都有一个根对象,就是Super POM。Super POM中定义了所有的默认的配置项。Super POM对应的pom.xml文件可以在maven安装目录下lib/maven-model-builder-3.0.3.jar:org/apache/maven/model/pom-4.0.0.xml中找到。
简单使用
仓库
Maven将所有的jar包都集中在一个叫做“仓库”的地方,一个Maven项目需要使用到某个jar包时会在其项目中指向这个jar包,具体表现就是pom.xml中添加了坐标。
仓库分为本地、私服、远程仓库。就像瓶子里没有盐了就去柜子里找,柜子里也没有的话那就只能去超市买了,瓶子、柜子、超市分别对应本地仓库、私服、远程公共仓库。
生命周期
maven拥有三套相互独立的生命周期,分别是clean,default和site。clean 生命周期的目的是清理项目 ,default 生命周期的作用是构建项目 ,site 生命周期的目的是构建项目站点 。每个生命周期都有各自的阶段,上面罗列出的是default的各个阶段。下面是几个比较重要的阶段:
clean生命周期
clean生命周期的目的是清理项目,它包含三个阶段:
- pre-clean 执行一些清理前需要完成的工作;
- clean 清理上一次构建生成的文件;
- post-clean 执行一些清理后需要完成的工作;
default生命周期
default生命周期定义了真正构建项目需要执行的所有步骤,它是所有生命周期中最核心的部分。其中的重要阶段如下:
- compile :编译项目的源码,一般来说编译的是src/main/java目录下的java文件至项目输出的主classpath目录中;
- test :使用单元测试框架运行测试,测试代码不会被打包或部署;
- package :接收编译好的代码,打包成可以发布的格式,如jar和war;
- install: 将包安装到本地仓库,共其他maven项目使用;
- deploy :将最终的包复制到远程仓库,供其他开发人员或maven项目使用;
site生命周期
site生命周期的目的是建立和发布项目站点,maven能够基于pom文件所包含的项目信息,自动生成一个友好站点,方便团队交流和发布项目信息。该生命周期中最重要的阶段如下:
- site :生成项目站点文档;
maven可以使用命令行直接调用命令。
Maven 命令:
mvn clean:调用clean生命周期的clean阶段,清理上一次构建项目生成的文件;
mvn compile :编译src/main/java中的java代码;
mvn test :编译并运行了test中内容 ;
mvn package:将项目打包成可发布的文件,如jar或者war包;
mvn install :发布项目到本地仓库 ;
pom.xml介绍
<!-- xml文档声明 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--
xmlns即xml name space, 本xml命名空间(命名空间出现是为了防止xml出现冲突)
xmlns:xsi, 约定了前缀为xsi对应的唯一字符串,即Schema约束(xsd)命名空间(业界默认)
xsi:schemaLocation, 这一行其实是 xsi:schemaLocation = "键" “值” 的形式,即 xsi 命名空间下 schemaLocation 元素的值为一个由空格分开的键值对。
不过这些定义不是太重要
-->
<!-- project 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>: 声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,目前POM模型版本是4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- <groupId> :maven项目组织id标识符,一般是公司域名倒过来写 -->
<groupId>com.bilibili.maven</groupId>
<!-- <artifactId>:项目的标识符,即项目名 -->
<artifactId>maven-web2</artifactId>
<!-- <version>:项目的版本号 -->
<version>1.0-SNAPSHOT</version>
<!-- <packaging>:maven项目的打包方式一般配置jar或者war -->
<packaging>war</packaging>
</project>
xml头部声明参考这篇博客
对于jar、war:
Jar文件(扩展名为. Jar,Java Application Archive)包含Java类的普通库、资源(resources)、辅助文件(auxiliary files)等
War文件(扩展名为.War,Web Application Archive)包含全部Web应用程序。在这种情形下,一个Web应用程序被定义为单独的一组文件、类和资源,用户可以对jar文件进行封装,并把它作为小型服务程序(servlet)来访问。
Ear文件(扩展名为.Ear,Enterprise Application Archive)包含全部企业应用程序。在这种情形下,一个企业应用程序被定义为多个jar文件、资源、类和Web应用程序的集合。
每一种文件(.jar, .war, .ear)只能由应用服务器(application servers)、小型服务程序容器(servlet containers)、EJB容器(EJB containers)等进行处理。
依赖管理
Maven的一个核心功能就是管理项目依赖。在<dependencies>
标签中可以添加如下三个参数就能确定一个依赖的具体信息
<dependency>
<!-- 项目组 -->
<groupId>mysql</groupId>
<!-- 项目名 -->
<artifactId>mysql-connector-java</artifactId>
<!-- 版本号 -->
<version>5.1.47</version>
</dependency>
在idea的pom.xml文件中用快捷键alt + insert
可以快速添加本地依赖。
本地没有的话可以在这个网站搜索
依赖配置
添加依赖之后还需要对依赖进行配置,比如确定 依赖范围、依赖版本维护等。
Maven中有以下5种典型的依赖范围:
- compile :编译依赖范围。如果没有指定,默认就是这种依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
- test :测试依赖范围。使用此依赖范围的Maven依赖,只对测试classpath有效。在编译主代码和运行项目期间都不需要使用此依赖。典型的例子就是Junit,它只在编译和运行测试代码时有效。
- provided :已提供依赖范围。使用此依赖范围的maven依赖在编译和测试classpath有效,但运行时无效。典型的例子就是servlet-api。编译和测试的时候需要使用servlet-api中的方法,但是使用tomcat运行项目的时候不需要,因为tomcat在运行期间会提供这个依赖。
- runtime: 运行时依赖范围。使用次依赖范围的maven依赖对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要提供JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
- system :系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量。
system 类型的依赖有个典型的例子就是oracle的驱动包,从中央仓库无法下载,需要先下载到本地,再通过本地路径引入。
<dependency>
<groupId>oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>1.0</version>
<scope>system</scope>
<!-- 必须指定systemPath -->
<systemPath>D:/software/maven/apache-maven-3.5.2/repository/ojdbc6.jar</systemPath>
</dependency>
依赖范围与classpath的关系如下:
依赖 | 对于编译classpath有效 | 对于测试classpath有效 | 对于运行时classpath有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | - | Junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | JDBC驱动 |
system | Y | Y | - | 本地的,maven仓库之外的类库 |
指定依赖的方式很简单,只需在<dependency>
标签中使用<scope>
设置即可。
示例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<!--compile是默认的依赖范围,可以不用写出来-->
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>D:/software/maven/apache-maven-3.5.2/repository/ojdbc6.jar</systemPath>
</dependency>
如果pom.xml文件中的依赖太多不便于统一管理,因此可以将版本号抽象出来同一管理:
示例
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<struts.version>2.3.8</struts.version>
<!-- IDEA中输入junit.version自动补全为下面的方式 -->
<junit class="version">4.11</junit>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts.version}</version>
</dependency>
</dependencies>
插件
如上面所说,Maven核心并没有什么功能,它的本质就是一个插件框架,所有的功能都是由插件来完成。同时官方提供了一些默认的插件,如果需要配置自己的插件,也可使引入坐标来实现。
官方的插件列表
http://maven.apache.org/plugins/index.html
http://mojo.codehaus.org/plugins.html
一个编译插件的例子:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
清理文件
由于网络原因仓库中可能存在以lastUpdated
结尾的文件导致jar包导入失败,用everything搜一下删除就好了。
更新依赖
有时候配置了仓库后找不到jar包,此时需要在idea中更新仓库依赖:setting -> build -> buildTools -> maven -> repositories -> update
具体pom.xml的细节可以百度(。・ω・。)
这里放一个设置maven使用jdk 1.8的设置:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>