项目管理问题

常规的jar包管理主要存在以下的问题:

  1. 繁琐:要为每个项目手动导入所需的jar,需要搜集全部jar
  2. 复杂:项目中的jar如果需要版本升级,就需要再重新搜集jar
  3. 冗余: 相同的jar在不同的项目中保存了多份

那么怎么样才能方便快捷的管理我们项目中的jar包呢?

Maven概述

目前开发中常用的项目管理工具主要就是:MavenGradle

Maven(/ˈmeɪvn/):Maven这个单词来自于意第绪语(犹太语),意为知识的积累.

Maven是一个基于项目对象模型(POM)的概念的纯java开发的开源的项目管理工具。主要用来管理java项目,进行依赖管理(jar包依赖管理)和项目构建(项目编译、打包、测试、部署)。此外还能分模块开发,提高开发效率。

Gradle([ɡredl]):是一个基于JVM的构建工具,是一款通用灵活的构建工具,支持传递性依赖管理,而不需要远程仓库或者是pom.xml和ivy.xml配置文件,基于Groovy,build脚本使用Groovy编写。

Maven安装

  1. 下载链接:http://maven.apache.org/download.cgi ,历史版本: https://archive.apache.org/dist/maven/maven-3/

  2. 将下载的文件,选择合适的目录进行解压即可。 注意: 解压文件尽量不要放在含有中文或者特殊字符的目录下

    1
    2
    3
    4
    bin:含有mvn运行的脚本
    boot:含有plexus-classworlds类加载器框架,Maven 使用该框架加载自己的类库。
    conf:含有settings.xml配置文件
    lib:含有Maven运行时所需要的java类库
  3. 环境变量设置

    1
    2
    3
    # maven依赖java环境,所以要确保java环境已配置好 (maven-3.3+ 需要jdk7+)
    MAVEN_HOME = maven的安装目录
    PATH = maven的安装目录下的bin目录
  4. 查看maven版本信息

    1
    2
    3
    4
    5
    6
    C:\Users\sfuli>mvn -v
    Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T03:00:29+08:00)
    Maven home: D:\scoop\apps\maven\current\bin\..
    Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: D:\scoop\apps\oraclejdk8\current\jre
    Default locale: zh_CN, platform encoding: GBK
    OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Maven配置

maven的conf目录中有 settings.xml ,是maven的配置文件,做如下配置:

本地仓库

1
2
3
4
5
6
7
8
9
10
11
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<!-- 选择一个磁盘目录,作为本地仓库 -->
<localRepository>D:\scoop\persist\maven\repository</localRepository>

JDK配置

在profiles标签中增加 一个profile标签,限定maven项目默认的jdk版本.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<profiles>
<!-- 在已有的profiles标签中添加profile标签 -->
<profile>
<id>myjdk</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
<!-- 让增加的 profile生效 -->
<activeProfiles>
<activeProfile>myjdk</activeProfile>
</activeProfiles>

仓库

  • 存储依赖的地方,体现形式就是本地的一个目录。

  • 仓库中不仅存放依赖,而且管理着每个依赖的唯一标识(坐标),Java项目凭坐标获取依赖。

  • 当需要依赖时,会从仓库中取查找,优先顺序为:本地仓库 > 私服(如果配置了的话) > 公共仓库(如果配置了的话) > 中央仓库

  • Maven 中央仓库是由 Maven 社区提供的仓库,不用任何配置。 提供仓库搜索服务

  • 中央仓库在国外,下载依赖速度过慢,所以都会配置一个国内的公共仓库替代中央仓库。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--setting.xml中添加如下配置-->
    <mirrors>
    <mirror>
    <id>aliyun</id>
    <!-- 中心仓库的 mirror(镜像) -->
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <!-- aliyun仓库地址 以后所有要指向中心仓库的请求,都会指向aliyun仓库-->
    <url>https://maven.aliyun.com/repository/central</url>
    </mirror>

    <mirror>
    <id>aliyunmaven</id>
    <mirrorOf>*</mirrorOf>
    <name>阿里云公共仓库</name>
    <url>https://maven.aliyun.com/repository/public</url>
    </mirror>

    </mirrors>

私服

公司范围内共享的仓库,不对外开放。 可以通过 Nexus来创建、管理一个私服。

Idea-Maven

在idea中关联本地安装的maven,后续就可以通过idea使用maven,管理项目啦。

Idea中关联Maven

创建Maven项目

项目结构

  • src/main/java 存放源代码,建包,放项目中代码(service,dao,User,….)
  • src/main/resources 书写配置文件,项目中的配置文件(jdbc.properties)
  • src/test/java 书写测试代码,项目中测试案例代码
  • src/test/resources 书写测试案例相关配置文件
  • 项目根/pom.xml (project object model) maven项目核心文件,其中定义项目构建方式,声明依赖等

创建web项目

  1. pom.xml中设置 war

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?xml version="1.0" encoding="UTF-8"?>
    <project ...>
    ...
    <packaging>war</packaging>

    <!-- 导入JSP 和 Servlet 和 JSTL 依赖 -->
    <dependencies>
    <dependency>
    <!-- jstl 支持 -->
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    <dependency>
    <!-- servlet编译环境 -->
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <!-- jsp编译环境 -->
    <groupId>javax.servlet</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>
    </project>
  2. 按照maven规范,新建web项目特有目录webapp. 新建src\main\webapp\WEB-INF\web.xml

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <!-- 这是一个空白的web.xml文件模板 -->
    </web-app>

依赖生命周期

Jar包生效的时间段,即Jar的生命周期

标识 周期
compile 缺省值,适用于所有阶段(测试运行,编译,运行,打包)
provided 类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet-api.jar;适用于(测试运行,编译)阶段
runtime 只在运行时使用,如 mysql的驱动jar,适用于(运行,测试)阶段
test 只在测试时使用,适用于(编译,测试运行)阶段,如 junit.jar
system Maven不会在仓库中查找对应依赖,在本地磁盘目录中查找;适用于(编译,测试运行,运行)阶段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version> <!-- 生命周期 -->
<scope>compile</scope>
</dependency>
<dependency> <!-- servlet编译环境 -->
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version> <!-- 生命周期 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version> <!-- 生命周期 -->
<scope>test</scope>
</dependency>

Maven指令

  1. compile 编译
  2. clean 删除target/
  3. test 运行测试test case junit/testNG
  4. package 打包
  5. install 把项目install到local repo
  6. deploy 发本地jar发布到remote

私服

搭建私服

  • 所谓私服就是一个服务器,但是不是本地层面的,是公司层面的,公司中所有的开发人员都在使用同一个私服,有了私服之后,当 Maven 需要下载依赖时,直接请求私服,私服上存在则下载到本地仓库;否则,私服请求外部的远程仓库,将构件下载到私服,再提供给本地仓库下载。
  • 私服可以解决在企业做开发时每次需要的jar包都要在中心仓库下载,且每次下载完只能被自己使用,不能被其他开发人员使用的问题
  • 我们可以使用专门的 Maven 仓库管理软件来搭建私服,比如:Apache ArchivaArtifactorySonatype Nexus。这里我们使用 Sonatype Nexus
  1. 下载: 请根据自己需要,选择如下对应版本(目前 Nexus 分为 Nexus 2.x 和 Nexus 3.x 两个大版本,目前使用最多,运行最稳定的是 Nexus 2.x)

  2. 从nexus官网下载地址:https://www.sonatype.com/download-oss-sonatype

  3. 可以使用wget下载方式,命令如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 一般远程调用下载软件 路径类型是http;如果下载的路径类型是https,则需要一个认证指令,即wget 后面加上--no-check-certificate
    wget --no-check-certificate https://download.sonatype.com/nexus/3/latest-unix.tar.gz
    # 解压到当前文件夹下:
    tar -xzvf nexus-3.19.1-01-unix.tar.gz

    # 解压后在bin目录中执行:
    nexus install 在系统中安装nexus服务
    nexus uninstall 卸载nexus服务
    nexus start 启动服务
    nexus stop 停止服务
  4. 修改配置文件

    1
    2
    3
    4
    5
    cd /home/maven/nexus3/nexus-3.19.1-01/etc
    vi nexus-default.properties

    # 修改host为对应服务器的ip
    application-host=192.168.61.10
  5. 启动nexus

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cd /home/maven/nexus3/nexus-3.19.1-01/bin
    # 启动nexus
    ./nexus start

    # 查看启动日志
    cd /home/maven/nexus3/sonatype-work/nexus3/log
    tail -f nexus.log
    # 查看链接日志
    tail -f nexus-request.log
  6. 登录nexus manager:http://192.168.61.10:9998/nexus/ 用户名默认admin,密码存在/home/maven/nexus3/sonatype-work/nexus3/ 下的admin.password文件中。

Maven配置私服

配置settings.xml,设置私服地址、认证等信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<servers>
<server>
<id>nexus-public</id> <!-- nexus的认证id -->
<username>admin</username> <!--nexus中的用户名密码-->
<password>admin123</password>
</server>
</servers>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>nexus-public</id> <!--nexus认证id 【此处的repository的id要和 <server>的id保持一致】-->
<!--name随便-->
<name>Nexus Release Snapshot Repository</name>
<!--地址是nexus中仓库组对应的地址-->
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories> <!--插件仓库地址,各节点的含义和上面是一样的-->
<pluginRepository>
<id>nexus-public</id> <!--nexus认证id 【此处的repository的id要和 <server>的id保持一致】-->
<!--地址是nexus中仓库组对应的地址-->
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>yourjdk</activeProfile>
<!-- 使私服配置生效 -->
<activeProfile>nexus</activeProfile>
</activeProfiles>

Maven 项目中依赖的搜索顺序

网上有很多关于maven项目中mirror、profile、repository的搜索顺序的文章,说法不一。官方文档并没有找到相关的说明,鉴于此,我抽时间做了一个验证。

依赖仓库的配置方式

maven项目使用的仓库一共有如下几种方式:

  1. 中央仓库,这是默认的仓库
  2. 镜像仓库,通过 sttings.xml 中的 settings.mirrors.mirror 配置
  3. 全局profile仓库,通过 settings.xml 中的 settings.repositories.repository 配置
  4. 项目仓库,通过 pom.xml 中的 project.repositories.repository 配置
  5. 项目profile仓库,通过 pom.xml 中的 project.profiles.profile.repositories.repository 配置
  6. 本地仓库
    如果所有配置都存在,依赖的搜索顺序就会变得异常复杂。

分析依赖搜索顺序

先从最简单开始,慢慢增加配置,查看有什么变化。

准备测试环境

安装jdk、maven。

使用如下命令创建测试项目:

1
yes | mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp  -DinteractiveMode=true -DgroupId=com.pollyduan -DartifactId=myweb -Dversion=1.0 -Dpackage=com.pollyduan

创建完成后,为了避免后续测试干扰,先执行一次compile。

1
2
cd myweb
mvn compile

最后,修改 pom.xml 文件,将 junit版本号改为 4.12 。我们要使用这个jar来测试依赖的搜索顺序。

默认情况

首先确保junit4.12不存在:

1
rm -rf ~/.m2/repository/junit/junit/4.12

默认情况下没有配置任何仓库,也就是说,既没改 $M2_HOME/conf/settings.xml 也没有添加 ~/.m2/settings.xml

执行编译,查看日志中拉取junit的仓库。

1
2
3
4
mvn compile

...
Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.12/junit-4.12.pom (24 kB at 11 kB/s)
  • 可以看出,默认是从 central 中央仓库拉取的jar.

配置镜像仓库 settings_mirror

创建 ~/.m2/setttings.xml ,内容如下:

1
2
3
4
5
6
7
8
9
<settings>
<mirrors>
<mirror>
<id>settings_mirror</id>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>

重新测试:

1
2
rm -rf ~/.m2/repository/junit/junit/4.12
mvn compile

在日志中查看下载依赖的仓库:

1
Downloaded from settings_mirror: https://maven.aliyun.com/repository/public/junit/junit/4.12/junit-4.12.pom (24 kB at 35 kB/s)
  • 可以看出,是从 settings_mirror 中下载的jar
  • 结论:settings_mirror 的优先级高于 central

配置pom中的仓库 pom_repositories

在 project 中增加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
<repositories>
<repository>
<id>pom_repositories</id>
<name>local</name>
<url>http://10.18.29.128/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
  • 由于我们改变了id的名字,所以仓库地址无所谓,使用相同的地址也不影响测试。

执行测试:

1
2
rm -rf ~/.m2/repository/junit/junit/4.12
mvn compile

在日志中查看下载依赖的仓库:

1
Downloaded from pom_repositories: http://10.18.29.128/nexus/content/groups/public/junit/junit/4.12/junit-4.12.pom (24 kB at 95 kB/s)

从显示的仓库id可以看出:

  • jar 是从 pom_repositories 中下载的。
  • pom_repositories 优先级高于 settings_mirror

配置全局profile仓库 settings_profile_repo

在 ~/.m2/settings.xml 中 settings 的节点内增加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<profiles>
<profile>
<id>s_profile</id>
<repositories>
<repository>
<id>settings_profile_repo</id>
<name>netease</name>
<url>http://mirrors.163.com/maven/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>

执行测试:

1
2
rm -rf ~/.m2/repository/junit/junit/4.12
mvn compile -Ps_profile

在日志中查看下载依赖的仓库:

1
Downloaded from settings_profile_repo: http://mirrors.163.com/maven/repository/maven-public/junit/junit/4.12/junit-4.12.pom (24 kB at 63 kB/s)

从显示的仓库id可以看出:

  • jar 是从 settings_profile_repo 中下载的。
  • settings_profile_repo 优先级高于 settings_mirror。
  • settings_profile_repo 优先级高于 pom_repositories 。

配置项目profile仓库 pom_profile_repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<profiles>
<profile>
<id>p_profile</id>
<repositories>
<repository>
<id>pom_profile_repo</id>
<name>local</name>
<url>http://10.18.29.128/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>

执行测试:

1
2
3
rm -rf ~/.m2/repository/junit/junit/4.12
mvn compile -Ps_profile,p_profile
mvn compile -Pp_profile,s_profile

在日志中查看下载依赖的仓库:

1
Downloaded from settings_profile_repo: http://mirrors.163.com/maven/repository/maven-public/junit/junit/4.12/junit-4.12.pom (24 kB at 68 kB/s)

从显示的仓库id可以看出:

  • jar 是从 settings_profile_repo 中下载的
  • settings_profile_repo 优先级高于 pom_profile_repo

进一步测试:

1
2
rm -rf ~/.m2/repository/junit/junit/4.12
mvn compile -Pp_profile

在日志中查看下载依赖的仓库:

1
Downloaded from pom_profile_repo: http://10.18.29.128/nexus/content/groups/public/junit/junit/4.12/junit-4.12.pom (24 kB at 106 kB/s)

从显示的仓库id可以看出:

  • jar 是从 settings_profile_repo 中下载的
  • pom_profile_repo 优先级高于 pom_repositories

最后确认 local_repo 本地仓库 ~/.m2/repository

这不算测试了,只是一个结论,可以任意测试。

  • 只要 ~/.m2/repository 中包含依赖,无论怎么配置,都会优先使用local本地仓库中的jar.

最终结论

  • settings_mirror 的优先级高于 central

  • settings_profile_repo 优先级高于 settings_mirror

  • settings_profile_repo 优先级高于 pom_repositories

  • settings_profile_repo 优先级高于 pom_profile_repo

  • pom_profile_repo 优先级高于 pom_repositories

  • pom_repositories 优先级高于 settings_mirror

  • 通过上面的比较得出完整的搜索链:local_repo > settings_profile_repo > pom_profile_repo > pom_repositories > settings_mirror > central

设置Maven内存

  • windows修改mvn.bat(mvn.cmd)文件

    1
    2
    3
    4
    # 找到@REM MAVEN_OPTS... 在下面添加
    set "MAVEN_OPTS=-Xss64m -Xmx2g -XX:ReservedCodeCacheSize=1g"

    # maven -v 可以看到set命令被打印
  • linux,修改mvn文件,添加

    1
    export MAVEN_OPTS="-Xss64m -Xmx2g -XX:ReservedCodeCacheSize=1g"