一文带你掌握Java开发利器:Maven

Posted 风在哪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文带你掌握Java开发利器:Maven相关的知识,希望对你有一定的参考价值。

Maven

如果作为一个Java程序员,那么在日常的开发过程中,maven是很常见的项目构建工具。maven可以极大的提高我们的开发效率,帮助我们简化开发过程中一些解决依赖和项目部署的相关问题,所以学习掌握maven的相关知识是非常有必要的。

本文从入门安装开始,逐步深入讲解maven的相关知识。

1、安装maven

首先我们需要准备好安装包,这里推荐在官网下载:maven官网下载地址

我这里下载的是3.8.1版本的maven,不同版本的maven配置过程基本是一样的。

1.1 Windows安装

首先将maven安装包解压,根据自己的习惯选择解压目录,后续配置需要使用到解压的目录。我这里解压到了D:\\software\\Maven,之后的配置也是从这里开始的。

首先要配置Windows的环境变量,如果是最新版的Windows 10,那么右键单击属性后会出现如下页面,点击高级系统设置=>环境变量即可:

接下来就是配置系统变量,首先配置MAVEN_HOME,在系统变量窗口点击新建,然后输入变量名和变量值,变量名为MAVEN_HOME,变量值就是maven的安装路径,可以通过浏览目录找到自己的maven安装位置。

接下来配置系统变量中的path变量,首先选中path变量,然后点击编辑,弹出编辑path变量的窗口,点击新建,在最后一行输入如下参数:%MAVEN_HOME%\\bin

此时maven的环境变量已经配置完成了,可以打开cmd窗口,运行mvn -v查看配置是否成功。

1.2 Linux安装

将下载好的文件上传至云服务器,我这里直接上传到了/usr/local/目录下,然后直接将其解压

tar -zxvf apache-maven-3.8.1-bin.tar.gz

接下来就是配置环境变量,在/etc/profile文件中添加环境变量,内容如下:

export MAVEN_HOME=/usr/local/apache-maven-3.8.1
export PATH=$PATH:$MAVEN_HOME/bin

然后再运行source /etc/profile使其生效即可。

然后可以运行mvn -v命令查看是否配置成功:

1.3 简单配置

当安装完maven以后,我们还需要配置maven的镜像和我们本地仓库的地址,maven的全局配置文件是安装目录下的conf/settings.xml文件,下面的配置都是在该文件中进行的。

1.3.1 配置本地仓库路径

本地仓库是我们新建maven项目并添加依赖后,那些依赖的jar包下载到的位置。

首先看看配置文件,本地仓库路径不配置的话默认为当前用户目录下的./m2文件夹下的repository目录,我们最好自己配置下,管理maven仓库的位置。

配置的话选定自己的仓库目录,然后添加在配置文件中即可,例如我这里是这样配置的:

<localRepository>D:\\software\\Maven\\MavenRepository</localRepository>

Linux的话需要注意路径的方式:

<localRepository>/usr/local/apache-maven-3.8.1/MavenRepository</localRepository>

1.3.2 配置镜像

如果我们不配置国内镜像的话,那么maven下载依赖可能会非常非常慢,所以我们这里要配置镜像,这里我配置的是阿里云的镜像:

<mirrors>
	 <mirror>  
	   <id>alimaven</id>
	   <name>aliyun maven</name>
       <url>https://maven.aliyun.com/repository/public</url>
       <mirrorOf>central</mirrorOf> 
	</mirror>
  </mirrors>

当然也可以配置些其他的镜像,例如华为的镜像。

阿里云maven华为云镜像

1.4 idea配置

我们通常都是使用idea进行项目的开发工作,所以这里接着介绍下idea如何配置我们自己安装的maven,而不是使用idea自带的maven。

首先点击Files->settins打开配置窗口:

然后找到maven的配置,将maven的安装路径和配置文件以及本地仓库的配置均修改为我们自己安装的maven即可:

此外还要配置每个新建项目的maven配置,点击New Projects Settings -> Settings for New Projects:

也是进行相同的配置:

配置完成以后,我们以后就可以使用自己安装的maven在idea中开发项目了。

2、Maven简单上手

虽然我们可以使用idea简化项目的开发,但是我们还是要简单了解一下不借助idea如何新建maven项目的。

首先来看看maven的项目结构:

src
 |--main
	|--java 源代码目录
	|--resources 资源目录
 |--test
	|--java 测试代码目录
	|--resources 测试资源目录
target
	|--classes 编译后的class文件目录
	|--test-classes 编译后的测试class文件目录
pom.xml Maven工程配置文件

其中src、target、pom是同级目录,接下来我们尝试手动创建这些目录:

我们首先来看看pom.xml的基本配置:

<?xml version="1.0" encoding="UTF-8"?>
<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>4.0.0</modelVersion>

    <groupId>cn.wygandwdn</groupId>
    <artifactId>learn_maven</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

然后接着在src/java目录下新建一个Hello.java:

import java.util.*;

class Hello 
    public static void main(String[] args) 
        System.out.println("Hello Maven");
    

然后在项目的根目录下运行mvn compile试试,也就是src文件对应的目录:

出现BUILD SUCCESS就是构建成功了,接下来看看都构建出来了哪些文件:

可以看出,maven自动为我们将Hello.java构建成了Hello.class并放到了target/classes目录下。

是不是很简单,当我们使用idea之后,这些工作都不需要我们来做,都是自动化完成的。

3、Maven生命周期

如果我们打开idea,查看maven的话,会发现maven的几个生命周期:

这些生命周期实际上分为三部分,分别是:

  • clean:为执行以下工作做必要的清理,就是删除out文件夹。
  • default:真正进行项目编译打包等工作的阶段
  • site: 生成项目报告,站点,发布站点

这三个生命周期又有各自详细的生命周期。

clean生命周期又分为如下几个阶段:

  • pre-clean:执行一些需要在clean之前完成的工作
  • clean:移除所有上一次构建生成的文件
  • post-clean:执行一些需要在clean之后立刻完成的工作

default又分为如下几个阶段(和idea中显示的正好对应,这是简化版的生命周期):

  1. validate:验证项目是否正确,所有必要的信息是否可用
  2. compile:编译项目的源代码
  3. test:使用合适的单元测试框架测试编译的源代码,测试代码不应该被打包或者部署
  4. package:将编译后的代码打包成可发布的格式,例如jar包
  5. verify:运行任意的检查来验证项目包是否有效且达到质量标准
  6. install:安装项目包到本地仓库,这样项目包可以用做其他本地项目的依赖
  7. deploy:将最终的项目包复制到远程仓库与其他开发者和项目共享

site生命周期又分为如下几个阶段:

  • pre-site:执行一些需要在生成站点文档之前完成的工作
  • site:生成项目的站点文档
  • post-site: 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
  • site-deploy:将生成的站点文档部署到特定的服务器上

各个生命周期相互独立,互不影响,一个生命周期的阶段前后依赖。

当我们运行maven命令时,例如运行mvn compile,只有该阶段之前以及包括该阶段在内的所有阶段才会被执行,在compile之后的test、package等是不会被执行的。

常用的maven命令:

命令说明
mvn -v显示版本信息
mvn clean清理项目产生的临时文件,一般是模块下的target目录
mvn compile编译源代码,一般编译模块下的src/main/java目录
mvn package项目打包工具,会在target目录下生成jar包或者war包
mvn test测试命令,或执行src/test/java/下junit的测试用例
mvn install将打包的jar/war文件复制到本地仓库,供其他模块使用
mvn deploy将打包的文件发布到远程服务器,供其他人员下载依赖
mvn site生成项目相关信息的网站
mvn dependency:tree打印出项目的整个依赖树
mvn archetype:generate创建maven的普通Java项目

4、POM文件详解

4.1、基础介绍

首先我们来看看一个简单的POM文件:

<?xml version="1.0" encoding="UTF-8"?>
<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>4.0.0</modelVersion>    
	<groupId>cn.wygandwdn</groupId>    
	<artifactId>learn_maven</artifactId>    
	<version>1.0-SNAPSHOT</version>    
	<packaging>pom</packaging></project>
</project>

这是maven项目构建后产生的最基础的pom文件,接下来讲讲这几个标签的含义:

  • modelVersion:描述pom文件遵循哪个版本的项目描述符,描述了当前pom模型的版本,对于maven2和maven3来说,它只能是4.0.0;它是强制性的

接下来的groupId、artifactId、version是唯一定位一个项目的,相当于项目的坐标:

  • groupId:团体组织的标识符,它以创建这个项目的组织名称的逆向域名开头,例如我们域名为wygandwdn.cn,这里就是cd.wygandwdn,同时也对应着Java的包结构
  • artifactId:单独项目的标识符,一个组织可能会有多个项目,那么这个artifactId就是描述一个组织名下的不同项目,不要在artifactId中包含点号(.)
  • version:项目的版本
  • packing:项目的打包类型,默认为jar,描述了项目打包后的输出。类型为jar的项目产生一个jar文件,类型war的项目产生一个web应用;类型为pom的话,说明该项目为一个聚合项目,包含多个子项目

Maven的version中通常出现如下几个特殊的字符串:SNAPSHOT、LATEST、RELEASE,例如我们使用idea创建的默认maven项目的版本通常为:1.0-SNAPSHOT。各个版本的含义和逻辑为:

  • SNAPSHOT:表示项目开发过程中的快照版本,不稳定的版本
  • LATEST:某个特定构件的最新版本,这个发布可能是一个发布版,也可能是一个snapshot版本,具体看哪个时间最后
  • RELEASE:指最后一个发布版,稳定版

4.2、dependencies&dependency

我们之前说过,maven最终的功能就是管理依赖,那么这些依赖就是通过dependencies&dependency标签来定义的。

例如:

<?xml version="1.0" encoding="UTF-8"?><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>4.0.0</modelVersion>    
	<groupId>cn.wygandwdn</groupId>    
	<artifactId>learn_maven</artifactId>    
	<version>1.0-SNAPSHOT</version>    
	<packaging>pom</packaging>        
	<dependencies>        
	<!-- 每个dependency都对应这一个jar包 -->        
		<dependency>            
		<!--一般情况下,maven是通过groupId、artifactId、version这三个元素值(俗称坐标)来检            索该构件, 然后引入你的工程。如果别人想引用你现在开发的这个项目(前提是已开发完毕并发布到了远程仓库),-->            
		<!--就需要在他的pom文件中新建一个dependency节点,将本项目的groupId、artifactId、            version写入, maven就会把你上传的jar包下载到他的本地 -->            
			<groupId>junit</groupId>            
			<artifactId>junit</artifactId>            
			<version>4.12</version>            
			<!-- 默认值为jar,它通常代表依赖关系的文件名的扩展 -->            
			<type>jar</type>            
			<!-- 依赖范围 -->            
			<scope>complie</scope>            
			<!-- 设置 依赖是否可选,默认为false,即子项目默认都继承。如果为true,则子项目必需显示的引入 -->            
			<optional>false</optional>            
			<!-- 依赖排除-->            
			<exclusions>                
			<exclusion>                    
			<groupId>org.slf4j</groupId>                    
			<artifactId>slf4j-api</artifactId>                
			</exclusion>            
			</exclusions>        
			</dependency>    
		</dependencies>
</project>

上面的groupId、artifactId、version是定位jar包的坐标,通过这个唯一的标识,maven会自动去仓库引入依赖。

  • type:代表项目依赖的类型,例如jar、war、pom等。

  • scope:代表的是这些依赖的作用范围,分别如下:

    • compile(默认编译范围):该范围是默认依赖范围,此依赖范围对于编译、测试、运行三种classpath都有效,也就是说该范围下的jar包在编译、测试、运行和打包时都可以使用。

    • test(测试依赖范围):test范围的依赖只对测试classpath有效,在编译主代码和项目运行时,都将无法使用该依赖,最典型的例子就是junit。junit是测试时才需要的依赖,所以它的依赖范围需要指定为test。如果不加范围也不会报错,但是该依赖会被加入到编译和运行的classpath中,会浪费一定的空间

      <dependency>    
          <groupId>junit</groupId>    
          <artifactId>junit</artifactId>    
          <version>4.7</version>    
          <scope>test</scope>
      </dependency>
      
    • provided(已提供依赖):使用该依赖范围时,只对编译和测试的classpath有效,对运行时classpath无效,最典型的例子就是servlet-api,编译和测试项目时都需要该依赖,但是在运行时,web容器已经提供该依赖,所以运行时就不需要此以爱,如果不显示指定该依赖范围,并且容器依赖的版本和maven不一致的话,可能会造成冲突。

      <dependency>    
      	<groupId>javax.servlet</groupId>    
      	<artifactId>javax.servlet-api</artifactId>    
      	<version>4.0.1</version>    
      	<scope>provided</scope>
      </dependency>
      
    • runtime(运行时依赖范围):使用该依赖范围时,只对测试和运行的classpath有效,对编译时的classpath无效,典型的例子就是jdbc驱动的实现,项目主代码编译的时候只需要jdk提供的jdbc接口,只有在测试和运行时才需要实现上述接口的具体jdbc驱动。

      <dependency>    
          <groupId>mysql</groupId>    
          <artifactId>mysql-connector-java</artifactId>    
          <version>5.1.25</version>    
          <scope>runtime</scope>
      </dependency>
      
  • optional:设置依赖是否可选,默认为false,即子项目默认都继承。如果为true,则子项目必需显示的引入

  • exclusions:该标签主要用于依赖的排除,该标签包含s,说明可以排除多个依赖,具体排除的依赖通过exclusion标签定义,exclusion中通过groupId和artifactId定位要排除的依赖。

4.3、dependencyManagement

dependencyManagement通常会在父工程中定义,目的就是为了统一各个子模块的依赖版本,也就是管理子项目中依赖的jar包的版本。

  • 该标签只是声明依赖,并不是实际的引入

  • 子项目需要显式的声明需要用的依赖,如果不再子项目中声明依赖,那么子项目是不会从父项目中继承依赖的

  • 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本

在我们创建聚合工程时,使用该标签非常有用

4.4、properties

properties定义了一些在pom中使用的占位符,相当于一些公共的属性,一般用于指定各个依赖的版本号。

例如:

<?xml version="1.0" encoding="UTF-8"?>
<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>4.0.0</modelVersion>
    <groupId>cn.wygandwdn</groupId>
    <artifactId>learn_maven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    
	<properties>
        <mysql.version>8.0.22</mysql.version>
    </properties>
    
    <dependencies>
    		<dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>$mysql.version</version>
            </dependency>
    </dependencies>
    
</project>

当我们定义mysql连接依赖时,将其对应的版本通过properties来定义,那么以后修改mysql连接依赖时,只需要修改标签中的依赖即可,不需要再去对应的依赖处修改。

properties标签内一般根据依赖的名称来自定义标签,通过"."进行分割,然后在dependency中引入即可。

当我们引入众多的依赖时,使用properties非常有用,通过properties定义版本,那么修改时,只统一修改这里的版本即可。其子项目也可以使用properties里定义的标签。

4.5、构建配置

来看看build标签下的内容:

<build>
    <!-- 产生的构件的文件名,默认值是$artifactId-$version-->
    <finalName>myPorjectName</finalName>
    <!-- 构建产生的所有文件存放的目录,默认为$basedir/target,即项目根目录下的target -->
    <directory>$basedir/target</directory>
    <!--项目相关的所有资源路径列表,例如和项目相关的配置文件、属性文件,这些资源被包含在最终的打包文件里-->
    <!--项目源码目录,当构建项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径-->
    <sourceDirectory>$basedir\\src\\main\\java</sourceDirectory>
    <!--项目单元测试使用的源码目录,当测试项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径-->
    <testSourceDirectory>$basedir\\src\\test\\java</testSourceDirectory>
    <!--被编译过的应用程序class文件存放的目录-->
    <outputDirectory>$basedir\\target\\classes</outputDirectory>
    <!--被编译过的测试class文件存放的目录-->
    <testOutputDirectory>$basedir\\target\\test-classes</testOutputDirectory>
    <!-- 以上配置都有默认值,就是约定好了目录就这么建 -->
    <resources>
        <!--处理src中填写的配置文件,防止打包的时候它们被过滤掉-->
        <!--本初配置的含义就是不对src/main/java下的../*.xml资源进行筛选,对src/main/java下的../*.properties进行过滤-->
        <resource>
            <directory>src/main/java</directory>
            <!--指定要包含的文件作为指定目录下的资源-->
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <!--指定要忽略的资源,如果includes和excludes包含同样的文件,那么excludes中的定义生效-->
            <excludes>
              <exclude>**/*.properties</exclude>
            </excludes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
    <!--单元测试相关的所有资源路径,配置方法与resources类似 -->
    <testResources>
        <testResource>
            <targetPath />
            <filtering />
            <directory />
            <includes />
            <excludes />
        </testResource>
    </testResources>
    <!--使用的插件列表-->
    <plugins>
        <plugin>
        <!--具体在插件使用中了解-->
        </plugin>
    </plugins>
    <!--主要定义插件的共同元素、扩展元素集合,类似于dependencyManagement-->
    <!--所有继承于此项目的子项目都能使用。该插件配置项直到被引用时才会被解析或绑定到生命周期-->
    <!--给定插件的任何本地配置都会覆盖这里的配置-->
    <pluginManagement>
    	<plugins>...</plugins>
    </pluginManagement>

</build>

resources主要处理资源过滤的问题,这里详细解释下resource标签下的各个属性:

  • directory:资源过滤对应的目录
  • includes:资源过滤对哪些资源生效
  • excludes:资源过滤对哪些资源不生效
  • filtering:true或者false(具体情况可以自己在实际项目中测验一下,这里直接给出测验后的结论)
    • true就是对includes下定义的资源过滤,对excludes下定义的资源不过滤
    • false就是对includes下定义的资源不过滤,对excludes下定义的资源过滤

当我们想引入支付包的jar包或者oracle的连接驱动时,可以自己配置jar包的路径,然后引入项目,具体的配置如下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <!--是否被子项目继承,默认为true-->
    <inherited>true</inherited>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
        <compilerArguments>
            <!-- 本地jar,支付宝jar包放到 src/main/webapp/WEB-INF/lib 文件夹下,
            如果没有配置,本地没问题,但是线上会找不到sdk类
            为什么要引入,因为支付宝jar包再中央仓库没有,再比如oracle连接驱动的jar
            -->
            以上是关于一文带你掌握Java开发利器:Maven的主要内容,如果未能解决你的问题,请参考以下文章

厉害了,一文带你掌握 14 种 UML 图!

一文讲透Java核心技术之高可扩展利器SPI

一文讲透Java核心技术之高可扩展利器SPI

一文带你快速掌握FastJSON的使用

一文带你弄懂 Maven 拉包原理

变现利器!一文带你学会应用内添加「贴片广告」