maven 笔记

1. maven 笔记

1.1. Running a Maven Project from the Command Line (and Building Jar Files)

This tutorial will show you how to run a basic Java Maven project on the command line (as opposed to on an IDE).

We will learn how to:

  • Create a new maven project
  • Compile and execute your Java code into an executable JAR file
  • Add unit test for your code

All of these tasks will be done on the command line, so that you can have a better idea on what’s going on under the hood, and how you can run a java application in environments that don’t have a full-featured IDE like Eclipse or IntelliJ.

If you just want to see the example code, you can view it on Github

1.1.1. Creating a New Maven Project

If you haven’t already, install OpenJDK on your system, after which you can install maven.

First, let’s create a new project folder using the maven command line tools:

mvn -B archetype:generate -DgroupId=com.sohamkamani -DartifactId=mvn-example -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

I’ve used a groupID corresponding to my domain (sohamkamani.com), you should replace this with your own choice

This generates a new project folder with the following structure:

mvn-example
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── sohamkamani
    │               └── App.java
    └── test
        └── java
            └── com
                └── sohamkamani
                    └── AppTest.java

App.java contains simple code that prints Hello World! when run.

1.1.2. Compiling our JAR File

Before running a Java application in production, we’ll need to compile the Java code into byte-code that can be run on the JVM.

If we have multiple classes and folder (which we most likely will), we have to package the compiled code into a common format (like a .jar file).

We can perform compilation and packaging by running the following command:

mvn compile
mvn package

We can combine these two commands by running mvn package.

Since compile and package are part of the same lifecycle, running mvn package will execute all lifecycle steps upto package, which includes the compile step

Running this command will create a bunch of files in a new target directory:

mvn-example
├── pom.xml
├── src/...
└── target
    ├── classes
    │   └── com
    │       └── sohamkamani
    │           └── App.class
    ├── test-classes
    │   └── com
    │       └── sohamkamani
    │           └── AppTest.class
    └── mvn-example-1.0-SNAPSHOT.jar

Some auxillary files are omitted from here for the sake of clarity

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wQLUtsW-1686725337911)(null)]

The JAR file is the final output that can be executed by the JVM. However, we still have to perform some additional steps before we can run our code.

1.1.3. Running our Code

We can use the java command to execute our JAR file:

java -jar target/mvn-example-1.0-SNAPSHOT.jar

If we run this now, we will get the following error:

no main manifest attribute, in target/tmp-mvn-example-1.0-SNAPSHOT.jar

This is because the JAR file doesn’t know the entry point, so it has no idea where the main method is.

We can make use of the Maven JAR plugin, which gives us additional capabilities to build JAR files.

We can add the following configuration as a child of the <build> tag:

<!-- this goes within <build> -->
<plugins>
	<plugin>
		<!-- Build an executable JAR -->
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-jar-plugin</artifactId>
		<version>3.1.0</version>
		<configuration>
			<archive>
				<manifest>
					<addClasspath>true</addClasspath>
					<!-- here we specify that we want to use the main method within the App class -->
					<mainClass>com.sohamkamani.App</mainClass>
				</manifest>
			</archive>
		</configuration>
	</plugin>
</plugins>
<!-- other properties -->

We can now rebuild the project by running:

mvn clean package

The clean subcommand removes previous artifacts in the target directory, such as the previous stale JAR file

Next, we can execute the JAR file by running:

java -jar target/mvn-example-1.0-SNAPSHOT.jar

Which will give us the output:

Hello World!

1.1.4. Running Unit Tests

Maven can also be used to run tests that we’ve defined in our project.

By convention, all tests reside within the src/test directory.

For the purpose of illustration, let’s create a static method to add two numbers in the App class:

public class App {
    public static void main(String[] args) {
        System.out.printf("Hello World! %d", Calculator.add(4, 5));
    }

    public static int add(int n1, int n2) {
        return n1 + n2;
    }
}

We can now create a unit test for this method within src/test/java/com/sohamkamani/AppTest.java:

package com.sohamkamani;

// the JUnit library is used for testing
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class AppTest {

    @Test
    public void shouldAnswerWithTrue() {
        int result = App.add(3, 4);
        assertEquals(7, result);
    }
}

By default, the maven project folder comes bundled with the JUnit library for running unit tests

To run tests, we can run the mvn test command - this will run all tests, tell us how many passed and failed, and give us more information about the failed tests.

1.1.5. Adding Dependencies with the Maven Assembly Plugin

Let’s look at how to add dependencies and package them in our JAR file.

For most applications need external libraries (like Spring Boot or Apache Commons) to implement common functionality. Maven allows us to install these dependencies by specifying them in our pom.xml file.

For this example, let’s install the Cowsay library, which will display our output as a quote from a friendly cartoon figure of a cow.

First, we have to add Cowsay as a dependency in our pom.xml file:

<!-- ... -->

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!-- We can add additional dependencies here -->
    <dependency>
      <groupId>com.github.ricksbrown</groupId>
      <artifactId>cowsay</artifactId>
      <version>1.1.0</version>
      <classifier>lib</classifier>
    </dependency>
  </dependencies>

<!-- ... -->

Next, we can use the Cowsay.say method within our main method to print the final output string:

package com.sohamkamani;

// import Cowsay class from the library
import com.github.ricksbrown.cowsay.Cowsay;

public class App {
    public static void main(String[] args) {
        int result = Calculator.add(4, 5);
        // We can specify the arguments and get the display
        // string from the `Cowsay.say` method
        String[] cowArgs = new String[] { String.valueOf(result) };
        String cowString = Cowsay.say(cowArgs);

        // print the final output string
        System.out.printf(cowString);
    }

    public static int add(int n1, int n2) {
        return n1 + n2;
    }
}

However, there’s a problem - If we recompile our code and try to run the app now, we will get an error:

$ mvn clean compile package
$ java -jar target/mvn-example-1.0-SNAPSHOT.jar 
Exception in thread "main" java.lang.NoClassDefFoundError: com/github/ricksbrown/cowsay/Cowsay
        at com.sohamkamani.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: com.github.ricksbrown.cowsay.Cowsay
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        ... 1 more

It looks like the Java class loader couldn’t find the classes for the Cowsay library, even though we added it as a dependency in the pom.xml file.

This happens because by default, maven doesn’t bundle the dependency class files along with the application code. To enable this, we can use the maven-assembly-plugin.

This plugin includes all of our applications dependencies into the JAR file. This increases its overall size, but ensures that we can run it as a standalone executable using the java -jar command.

Let’s add the Maven assembly plugin in the pom.xml build definition:

<!-- ... -->
<build>
<plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <archive>
        <manifest>
            <addClasspath>true</addClasspath>
            <mainClass>com.sohamkamani.App</mainClass>
        </manifest>
        </archive>
    </configuration>
    </plugin>
    <!-- Add the assemble plugin with standard configuration -->
    <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <archive>
        <manifest>
            <mainClass>com.sohamkamani.App</mainClass>
        </manifest>
        </archive>
        <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
    </plugin>
</plugins>
<!-- ... -->
</build>
<!-- ... -->

To assemble our JAR file, we can run the assembly:single goal after the compile goal:

mvn clean compile assembly:single

This creates a new JAR file in the target directory that you can run using the java -jar command:

$ java -jar target/mvn-example-1.0-SNAPSHOT-jar-with-dependencies.jar
 ___
< 9 >
 ---
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

You can view the complete example code for this post on Github

1.2. Maven 下载镜像

如果一个仓库 A 可以提供另一个仓库 B 的所有内容, 那么就可以认为仓库 A 是仓库 B 的一个镜像。即仓库 B 中的任何一个构件都能从它的镜像中获取。

1.2.1. 使用镜像代替中央仓库

国内开发人员由于网络原因, 直接从中央仓库下载构件时, 速度较慢或不稳定, 我们通常会使用中央仓库的国内镜像站来解决该问题。

配置 Maven 镜像的方法也非常的简单, 我们只需要在 Maven 安装目录中 setting.xml 文件的 mirrors 节点中, 使用 mirror 标签添加镜像的相关信息即可。

目前国内使用最多, 最稳定的中央仓库镜像分别是由阿里云华为云提供的, 它们的地址配置如下。

NOTE 1: 笔者的实际使用体验来说,以上两者的下载速度还不如官方默认的,所以如果官方的够快的话就别折腾这个了。
NOTE 2: 注意把下面的配置写在 mirrors 中间,并且要用空格键对齐,否则配置可能会不生效。

阿里云镜像地址

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

华为云镜像地址

<mirror>
    <id>huaweicloud</id>
    <name>mirror from maven huaweicloud</name>
    <mirrorOf>central</mirrorOf>
    <url>https://repo.huaweicloud.com/repository/maven/</url>
</mirror>

以上配置中, mirrorOf 的取值为 central, 表示该配置为中央仓库的镜像, 所有对于中央仓库的请求都会转到该镜像。当然, 我们也可以使用以上方式配置其他仓库的镜像。另外三个元素 idnameurl 分别表示镜像的唯一标识、名称和地址。

1.2.2. 镜像与 Maven 私服配合使用

镜像通常会和 Maven 私服配合使用, 由于 Maven 私服可以代理所有外部的公共仓库(包括中央仓库), 因此对于组织内部的用户来说, 使用一个私服就相当于使用了所有需要的外部仓库, 这样就可以将配置集中到私服中, 简化 Maven 本身的配置。这种情况下, 用户所有所需的构件都可以从私服中获取, 此时私服就是所有仓库的镜像。

<srttings>
...
    <mirrors>
        <mirror>
            <id>nexus</id>
            <mirrorOf>*</mirrorOf>
            <name>nexus</name>
            <url>http://localhost:8082/nexus/content/groups/bianchengbang_repository_group/</url>
        </mirror>
    </mirrors>
...
</settings>

以上配置中, mirrorOf 元素的取值为 * , 表示匹配所有远程仓库, 所有对于远程仓库的请求都会被拦截, 并跳转到 url 元素指定的地址。

为了满足一些较为复杂的需求, Maven 还支持一些更为高级的配置。

  • <mirrorOf>*</mirrorOf>: 匹配所有远程仓库。
  • <mirrorOf>external:*</mirrorOf>: 匹配所有远程仓库, 使用 localhostfile:// 协议的除外。即, 匹配所有不在本机上的远程仓库。
  • <mirrorOf>repo1,repo2</mirrorOf>: 匹配仓库 repo1repo2, 使用逗号分隔多个远程仓库。
  • <mirrorOf>*,!repo1</miiroOf>: 匹配所有远程仓库, repo1 除外, 使用感叹号将仓库从匹配中排除。

需要注意的是, 由于镜像仓库完全屏蔽了被镜像仓库, 当镜像仓库不稳定或者停止服务时, Maven 也无法访问被镜像仓库, 因而将无法下载构件。

1.2.3. Maven 私服搭建

能够帮助我们建立私服的软件被称为 Maven 仓库管理器(Repository Manager), 主要有以下 3 种:

  • Apache Archiva
  • JFrog Artifactory
  • Sonatype Nexus

其中, Sonatype Nexus 是当前最流行, 使用最广泛的 Maven 仓库管理器。您可以转到《Nexus 教程》学习 Maven 私服的搭建流程。

参考自: C 语言中文网 - Maven 镜像

1.3. maven 中 SNAPSHOT 和 RELEASE 的区别

首先, 要了解 maven 中的仓库分为两种, SNAPSHOT 快照仓库和 RELEASE 发布仓库。

  • SNAPSHOT 快照仓库: 可以理解为开发过程中经常修改的不稳定的仓库, 引用版本为 SNAPSHOT 的仓库时每次构建自己的应用时都会重新下载(这里说的是版本号不变的情况下, 你不可能更改点代码就去升一个版本吧)。
  • RELEASE 发布仓库: 可以理解为正式版稳定的仓库, 本地 repository 一般不会频繁更新 release 版本的缓存, 如果发现本地已有这个版本, 就不会去远程 Repository 下载最新的 RELEASE 版本。

一句话: SNAPSHOT 用于开发, RELEASE 用于发布。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云满笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值