Maven实战

551 阅读3分钟

主要内容介绍

什么是Maven

Maven翻译为知识的积累,也可以翻译为专家或者内行,是跨平台的项目管理工具,主要服务于Java平台的项目构建,依赖管理和项目信息管理.

Maven的安装与配置(本文主要针对mac用户)

1.maven的安装需要依赖JDK,首先需要确保本地JDK是安装成功的 通过输入下面命令可验证JDK是否安装成功:

JasonMacBook-Pro:~ Jason$ java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

获得以上信息说明JDK安装成功

2.Maven官网下载Maven安装包,地址maven.apache.org/ 下载apache-maven-3.6.3-bin.tar.gz

tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local/; //解压到指定的目录

3.添加环境变量 vim ~/.bash_profile添加以下内容:

export M2_HOME=/usr/local/maven
export PATH=$PATH:$M2_HOME/bin

执行source .bash_profile命令完成生效

4.验证Maven是否安装成功:

Jason-Pro:~ Jason$ mvn -v
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T23:06:16+08:00)
Maven home: /usr/local/maven
Java version: 1.8.0_231, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.5", arch: "x86_64", family: "mac"

说明已经安装成功了

Maven坐标

Maven坐标为任何一个构件明确定义唯一性,通过groupId,artifactId,version,packaging, classifier

下面先看一组坐标的定义

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <packaging>jar</packaging>
</dependency>

下面解释以下各坐标元素的定义:

  • groupId: 定义当前Maven项目所属的实际项目,一般命名都是反向一一对应 代表一种组织 机构和公司 比如groupId定义为:com.tech.java
  • artifactId:实际项目的名称
  • version: 指当前项目的所处版本
  • packaging: 主要定义Maven项目的打包方式
  • classifier:帮助定义构件输出的一些附属构件,该构件与主构件对应的

Maven坐标groupId,artifactId,version是必须的,packaging是可选的(默认为jar), classifier是不能直接定义的。

Maven依赖

Maven有以下几种依赖范围:

  • compile: 编译依赖范围,如果没有指定的话就会默认该依赖范围,适用于所有阶段会随着项目一起发布
  • test: 测试依赖范围,在测试和运行的时候都需要该依赖,典型的例子就是junit.jar,不会随项目发布
  • provided:已提供依赖范围,对于编译和测试classpath有效,但是运行时无效 典型例子是servlet-api.jar
  • runtime:运行时依赖范围,对于测试和运行classpath有效,但在编译主代码时无效 典型例子是JDBC驱动
  • import:导入依赖范围, 用于解决单继承的问题 代码非常简洁,只能用在dependencyManagement里面,并且type=pom的dependency
  • system: 系统依赖范围,本地一些jar 例如短信jar,maven不会在repositoy中查找它

比如项目中想引入外部的短信sms.jar

<dependency>
    <groupId>sms-jar</groupId>   
    <artifactId>sms-jar</artifactId> 
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/*.jar</systemPath> //jar路径
</dependency>

Maven依赖调解的原则

由于传递依赖我们有的时候会碰到maven jar冲突的问题下面介绍依赖调解的原则

  • 最短路径原则(直接依赖 > 传递依赖)
  • 声明优先原则(依赖路径的长度相等的情况下,采用声明优先的原则)
  • 排除原则(传递依赖冲突时,可以在不需要的jar的传递依赖中声明排除,使用来排除依赖)
  • 版本锁定原则(在配置文件pom.xml中先声明使用哪个版本的jar,声明后其他版本一律不依赖)
  • 可选依赖,通过标签声明可选依赖,表示该jar只会对该项目产生影响,该依赖不会传递

Maven生命周期

Maven生命周期主要为clean,default,site三大部分.

  • clean生命周期的目的是清理项目
  • default生命周期的目的是构建项目
  • site生命周期是建立和发布项目站点

clean生命周期

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

default生命周期

包含以下阶段:

  • validate
  • initialize
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources
  • compile 编译项目的主源码
  • process-classes
  • generate-test-sources
  • process-test-sources
  • generate-test-resource
  • process-test-resouces
  • test-compile
  • process-test-classes
  • test 使用单元测试框架运行测试,测试代码不会被打包或部署
  • prepare-package
  • package 接受编译好的代码,打包成可发布的格式 如JAR,WAR
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install 将包安装到Maven本地仓库,供本地其他Maven项目使用
  • deploy 将最终的包复制到远程仓库,供其他开发任意和Maven项目使用

site生命周期

  • pre-site 执行一些在生成项目站点之前需要完成的工作
  • site 生成项目站点文档
  • post-site 执行一些在生成项目站点之后需要完成的工作
  • site-deploy 将生成的项目站点发布到服务器上

Maven常用命令

列出项目所有依赖

mvn dependency:list

分析编译主代码和测试代码需要用到的依赖

mvn dependency:analyze

打印依赖的树形结构

mvn dependency:tree

打印依赖的树形结构信息最全

mvn dependency:tree -Dverbose

打印信息需要包含指定的jar

mvn dependency:tree -Dverbose -Dincludes=junit:junit

打印信息指定过滤的jar

mvn dependency:tree -Dverbose -Dexcludes=juit:junit

项目打包跳过测试

mvn clean package -DskipTests //跳过单元测试
mvn clean package -Dmaven.test.skip=true

强制让maven检查更新

mvn clean install -U;

多线程并行编译

mvn clean -Dmaven.compile.fork=true

多线程并行打包

mvn clean -Dmaven.package.fork=true

多线程并行安装jar到本地

mvn clean -Dmaven.install.fork=true

把jar发布到私服

mvn deploy
mvn deploy -U ;//更新最新jar包

maven多线程构建

mvn -T 4 clean install
mvn -T 1C clean install

参数说明:

  • -T 4是直接指定4个线程
  • -T 1C表示CPU线程的倍数

以Debug模式编译项目

mvn clean compile -X

使用maven来启动tomcat项目

mvn tomcat:run

Maven插件介绍

在介绍maven插件之前先解释几个概念

  • Phase: 可以理解为任务单元,lifecycle是总任务,phase就是总任务分出来的一个个子任务 执行phase实际执行的是goal,如果一个phase没有绑定,那么phase就不会被执行,一个lifecycle可以包含任意个phase,phase的执行是按顺序的,一个phase可以绑定很多个goal,至少为一个,没有goal的phase是没有意义的
  • Goal: 这是执行任务的最小单元, 一个goal是独立的,它可以被绑定到多个phase中去,也可以一个phase都没有。如果一个goal没有被绑定到任何一个lifecycle,它仍然可以直接被调用,而不是被lifecycle调用
  • Mojo: mojo是具体做事情的 可以简单理解为mojo为goal的实现类 它继承于AbstractMojo

Maven插件官网:

下面我们介绍如何编写插件

背景:写一个插件用于统计某个具体项目的文件个数和代码行数

1.引入相关依赖

       <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.5.0</version>
       </dependency>
       <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.5</version>
       </dependency>

这里需要注意在上面pom.xml设置packaging类型为maven-plugin

    <packaging>maven-plugin</packaging>

2.定义插件类需要继承AbstractMojo

@Mojo(name = "autoScanCount", requiresProject = false, defaultPhase = LifecyclePhase.PACKAGE)
public class AutoScanCode extends AbstractMojo {

    //存放扫描的文件
    private static List<String> fileList = new ArrayList<>();

    @Parameter(name = "currentBaseDir", defaultValue = "/workspace")
    private String currentBaseDir;

    @Parameter(name = "suffix", defaultValue = ".java")
    private String suffix;

    //代码行数
    private int lines = 0;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        autoScanDir(currentBaseDir);
        System.out.println("自动扫描代码插件总共扫描文件个数:" + fileList.size());
        System.out.println("自动扫描代码插件总共扫描代码行数:" + lines);
    }

    //扫描文件目录
    private void autoScanDir(String baseDir) {
        File files = new File(baseDir);

        for (File file : files.listFiles()) {
            //如果是目录 需要进行递归调用
            if (file.isDirectory()) {
                autoScanDir(file.getAbsolutePath());
            } else {
                if (file.getName().endsWith(suffix)) {
                    fileList.add(file.getName());
                    lines += countLine(file);
                }
            }
        }
    }

    //统计文件行数
    private int countLine(File file) {
        int codeLine = 0;

        try {
            BufferedReader reader = new BufferedReader(new FileReader(file));
            while (reader.ready()) {
                reader.readLine();
                codeLine++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return codeLine;
    }
}

3.将项目本地安装到本地仓库,将项目依赖引入到其他项目

 <dependencies>
        <dependency>
            <groupId>com.java.tech</groupId>
            <artifactId>auto-scan-code</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    //这里需要绑定
    <build>
        <plugins>
            <plugin>
                <groupId>com.java.tech</groupId>
                <artifactId>auto-scan-code</artifactId>
                <version>1.0-SNAPSHOT</version>
                <configuration>
                    //指定路径
                    <currentBaseDir>/workspace/gitrepo/java-tech-tutorial</currentBaseDir>
                    <suffix>.java</suffix>
                </configuration>
                <executions>
                    <execution>
                        <phase>clean</phase>
                        <goals>
                            <goal>autoScanCount</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

4.在当前项目执行mvn clean命令即可生效该插件,更多代码可参考项目demo

Maven仓库

本地仓库

本地仓库用于存放依赖文件的目录,默认情况本地仓库位于用户目录下的.m2/repository目录下

有的时候需要修改仓库地址: vim ~/.m2/repository

<localRepository>/usr/local/repo</localRepository>

Maven提倡约定优于配置,也叫COC(Conversion Over Configuration) Maven加载jar的顺序:~/.m2/setting.xml->maven/conf/setting.xml

  • 先去.m2/setting.xml下寻找,然后在去conf/setting.xml
  • 仓库顺序:本地仓库->私服->maven中央仓库

远程仓库(nexus)

远程仓库主要用于解决jar依赖的问题,还有因为网络下载慢,很多公司都会选择搭建自己内部的私服服务器,方便下载构件 首先默认会在本地仓库寻找构件,如果本地仓库没有会去私服下载到本地仓库,如果私服还没有的话,会去maven中央仓库下载.

市面上有比较多的仓库管理软件

  • Sonatype Nexus
  • Jarvana
  • MVNrepository

我们这里重点介绍一下Nexus的使用 Nexus私服的优势

  • 节省带宽
  • 加速maven构建
  • 部署第三方构件
  • 降低中央仓库的压力

Nexus仓库类型:

  • group(仓库组)
  • hosted(宿主):管理本地仓库
  • proxy(代理)
  • virtual(虚拟)

仓库的用途

  • Codehaus Snapshots:用来管理Codehaus Snapshot仓库的快照构件
  • Apache Snapshots:用来管理Apache Snapshot仓库快照构件
  • Central:该仓库为Maven中央仓库,用于下载和缓存中央仓库中的发布版本构件
  • Release:策略为Release的宿主类型仓库,用于部署组织内部结构的发布版本构件
  • Snapshots:策略为Snapshot的宿主类型仓库,用来部署组织内部结构的快照版本构件

nexus安装与启动

1.下载nexus 2.解压nexus文件到指定的目录

tar -zxvf nexus-public-release-3.21.0-05.tar.gz -C /usr/local/nexus

3.在/usr/local/nexus/bin/nexus文件中加入:

export RUN_AS_USER=root

4.运行./nexus start启动nexus

关于Nexus下sonatype-work文件目录介绍:

  • conf: 存放日志 私服权限等配置文件
  • indexer:存放索引文件,私服页面看到的jar都是通过索引去storage搜索的
  • storage:jar包存储文件
  • logs: 存放日志文件

Profile的使用

Profile所解决的问题是在不同的环境开发,比如开发环境,测试环境,生产环境,项目的配置可能会不一样,比如数据源配置、日志文件配置、以及一些软件运行过程中的基本配置 每次将软件部署到不同的环境时,都需要修改相应的配置文件,这样来回修改,很容易出错,而且浪费劳动力.

一般在pom.xml中配置

 <profiles>
        <profile>
            <!-- profileId 代表唯一-->
            <id>dev</id>
            <properties>
                <env>dev</env>
            </properties>
            <activation>
                <!-- 默认激活该profile节点-->
                <activeByDefault>true</activeByDefault>
            </activation>
            <build>
                <resources>
                    <resource>
                        <directory>src/main/resources/dev</directory>
                    </resource>
                    <resource>
                        <directory>src/main/resources</directory>
                    </resource>
                </resources>
            </build>
        </profile>
        <profile>
            <!-- 测试环境 -->
            <id>qa</id>
            <properties>
                <env>qa</env>
            </properties>
            <build>
                <resources>
                    <resource>
                        <directory>src/main/resources/qa</directory>
                    </resource>
                    <resource>
                        <directory>src/main/resources</directory>
                    </resource>
                </resources>
            </build>
        </profile>
        <profile>
            <!-- 生产环境 -->
            <id>prod</id>
            <properties>
                <env>prod</env>
            </properties>
            <build>
                <resources>
                    <resource>
                        <directory>src/main/resources/prod</directory>
                    </resource>
                    <resource>
                        <directory>src/main/resources</directory>
                    </resource>
                </resources>
            </build>
        </profile>
    </profiles>

通过maven命令参数指定环境打包

maven打包时通过-P参数 -P后面跟上profile id

mvn clean install -Pdev

聚合和继承

Maven的聚合特性能够把项目的各个模块聚合在一起构建,而Maven的继承特性则能帮助抽取各模块相同的依赖和插件等配置 在简化POM的同时,还能促进各个模块配置的一致性

聚合就是把项目的各个模块聚合在一起进行构建,比如本项目就是一个聚合

    <modules>
        <module>java8</module>
        <module>design-pattern</module>
        <module>mybatis</module>
        <module>maven</module>
    </modules>

继承就是把一些通用的jar定义在父pom.xml中

dependencyManagement与dependency区别

  • dependencyManagement只能出现在父pom中 是一种统一声明,统一版本号,子pom根据按需引用,是一种预定义
  • dependency只引用jar依赖的

distributionManagement

  • 用于指定maven分发构件的位置
  • maven是区别对待release版本的构件和snapshot版本的构件
    • release版本比较稳定,版本无法覆盖
    • snapshot为开发过程的版本,实时但不稳定 版本可以被覆盖

Jenkins环境搭建

Jenkins是持续集成平台统称CI(Continous Intergation)

搭建步骤如下: 1.下载jenkins.war包,地址: jenkins.io/zh/download…

2.修改jenkins的目录,打开tomcat的bin目录,编辑catalina.sh文件 在# OS specific support. $var must be set to either true or false添加下面内容:

JENKINS_HOME=/data/jenkins

3.编辑profile文件 vim /etc/profile将JENKINS_HOME加入到环境变量中

export JENKINS_HOME=/data/jenkins

执行source /etc/profile命令让配置生效

4.在/usr/local/tomcat/bin/catalina.sh下面加上下面内容:

CATALINA_OPTS="$CATALINA_OPTS -server -Djava.awt.headless=true -Xms1g -Xmx1g -Xss256k  -XX:NewSize=600m -XX:MaxNewSize=600m -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:-ReduceInitialCardMarks -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingPermOccupancyFraction=80 -XX:+ExplicitGCInvokesConcurrent -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -DJENKINS_HOME=/data/jenkins -Dhudson.model.DirectoryBrowserSupport.CSP="
CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=Asia/Shanghai -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8"

5.把jenkins.war放到tomcat目录下的webapps并运行tomcat

sh /usr/local/tomcat/bin/startup.sh 

此时jenkins已经启动,通过浏览器输入http://localhost:8080进行访问