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内部原理解析


简单使用

仓库

Maven将所有的jar包都集中在一个叫做“仓库”的地方,一个Maven项目需要使用到某个jar包时会在其项目中指向这个jar包,具体表现就是pom.xml中添加了坐标。

仓库分为本地、私服、远程仓库。就像瓶子里没有盐了就去柜子里找,柜子里也没有的话那就只能去超市买了,瓶子、柜子、超市分别对应本地仓库、私服、远程公共仓库。

生命周期

maven拥有三套相互独立的生命周期,分别是cleandefaultsite。clean 生命周期的目的是清理项目 ,default 生命周期的作用是构建项目 ,site 生命周期的目的是构建项目站点 。每个生命周期都有各自的阶段,上面罗列出的是default的各个阶段。下面是几个比较重要的阶段:

clean生命周期

clean生命周期的目的是清理项目,它包含三个阶段:

  1. pre-clean 执行一些清理前需要完成的工作;
  2. clean 清理上一次构建生成的文件;
  3. post-clean 执行一些清理后需要完成的工作;

default生命周期

​default生命周期定义了真正构建项目需要执行的所有步骤,它是所有生命周期中最核心的部分。其中的重要阶段如下:

  1. compile :编译项目的源码,一般来说编译的是src/main/java目录下的java文件至项目输出的主classpath目录中;
  2. test :使用单元测试框架运行测试,测试代码不会被打包或部署;
  3. package :接收编译好的代码,打包成可以发布的格式,如jar和war;
  4. install: 将包安装到本地仓库,共其他maven项目使用;
  5. deploy :将最终的包复制到远程仓库,供其他开发人员或maven项目使用;

site生命周期

​site生命周期的目的是建立和发布项目站点,maven能够基于pom文件所包含的项目信息,自动生成一个友好站点,方便团队交流和发布项目信息。该生命周期中最重要的阶段如下:

  1. 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有效例子
compileYYYspring-core
test-Y-Junit
providedYY-servlet-api
runtime-YYJDBC驱动
systemYY-本地的,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>

标签: Maven, Web

添加新评论