Maven(二):Maven的使用入门

使用入门

项目骨架

对于一个使用Maven来管理的项目,Maven提倡使用一个目录结构标准:

${basedir}	存放pom.xml和所有的子目录
${basedir}/src/main/java	项目的java源代码
${basedir}/src/main/resources	项目的资源,比如说property文件,springmvc.xml
${basedir}/src/test/java	项目的测试类,比如说Junit代码
${basedir}/src/test/resources	测试用的资源
${basedir}/src/main/webapp/WEB-INF	web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面
${basedir}/target	打包输出目录
${basedir}/target/classes	编译输出目录
${basedir}/target/test-classes	测试编译输出目录
Test.java	Maven只会自动运行符合该命名规则的测试类
~/.m2/repository	Maven默认的本地仓库目录位置

在这里插入图片描述

说到这里,可以提一下Archetype,Maven中利用Archetype来生成项目骨架,其实就是上面这些东西,会自动生成一个空白项目的目录,连pom.xml都给你生成了,异常贴心。

当然,如果你是用的IDE的话,项目骨架它自动就给你建好了,但如果你一定要体验一下通过命令行来生成项目骨架的话,也可以。

如果是Maven3的话,在指定的项目父目录执行:

mvn archetype:generate

这个过程实际上是运行插件maven-archetype-plugin的过程。

接着会输出很多很多很多东西,这是在向你展示可供选择的项目骨架,即Archetype,每个Archetype都有一个编号,我看了一下,编号都到2423了。。。

我们可以选择maven-archetype-quickstart,输入后回车,通过数字选择就可以。

然后maven会提示你输入要创建项目的groupID、artifactId、version以及包名package等。全部输入,然后确定Y:

在这里插入图片描述

目录已经建好了。

像是src/main/javasrc/test/javapom.xml这种,它都给你就建好了。

我设定的包名是org.wlh,于是它很贴心的给我生成了一个src/main/java/org/wlh/App.java来作为主文件。

Archetype可以帮我们迅速构建起项目的骨架,而且提供了多种模板,非常棒。

这个了解下就行,毕竟IDE都帮我们做好了。

pom.xml

类似Make的Makefile、Ant的build.xml一样,Maven项目的核心是pom.xml。

POM(Project Object Model,项目对象模型)定义了项目的基本信息,比如说项目如何构建、声明项目依赖等。

下面是一个极度原始的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>
    <!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
    <groupId>com.companyname.project-group</groupId>
 
    <!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
    <artifactId>project</artifactId>
 
    <!-- 版本号 -->
    <version>1.0</version>
    <name>Hello World Project</name>
</project>

第一行是XML头,指定了该xml文档的版本和编码方式。

其余是project元素,project是pom.xml的根元素。

modelVersion指定了当前pom模型的版本,对于Maven2及Maven3来说,它只能是4.0.0。

这段代码中最重要的是groupId、artifactId和version三行。这三个元素基本定义了一个项目的基本坐标。

  • groupId:定义了项目属于哪个组;
  • artifactId:定义了当前项目在组内的唯一ID;
  • version:当前项目的版本;
  • name:非必要,定义了一个对于用户更友好的项目名称。

正常来讲,应该把项目主代码放到src/main/java目录下,Maven会自动搜索这个目录来找到项目入口类。

其次,项目入口类的包名,应该跟POM中定义的groupId和artifactId保持一致,即包名为groupId.artifactId,这样子更加清晰,也方便Maven的自动搜索。当然,不一样也是可以的,对运行没啥大影响。

构建过程

入口类编写完毕后,就可以使用Maven进行编译了。

在根目录下运行命令:mvn clean compile

clean会告知Maven清理输出目录target/;

compile则告诉Maven编译项目主代码;

完成后,Maven会将项目主代码编译至target/classes目录。

比如说我现在声明一个主类Hello World:

package org.wlh.helloworld;
public class Hello {
    
    
	public static void main(String[] args) {
    
    
		System.out.println(new Hello().HelloMaven());
	}

	public String HelloMaven(){
    
    
		return "Hello Maven";
	}
}

编写测试

Maven中测试代码应该放在src/test/java目录下。

Java项目中常用的单元测试框架JUnit。使用时需要在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>org.wlh</groupId>
	<artifactId>helloworld</artifactId>
	<version>1.0-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

代码中添加了dependencies元素,该元素下可以包含多个dependency元素以声明项目的依赖,一个dependency就是一个依赖。

dependency元素下的scope元素,表示该依赖的依赖范围

  • test:只对测试代码有效,在主代码中使用会报编译异常;
  • compile:默认的依赖范围,表示主代码和测试代码都能使用。

在src/test/java下编写测试类HelloTest:

package org.wlh.helloworld;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class HelloTest {
    
    
	@Test
	public void testHello(){
    
    
		Hello hello = new Hello();
		String result = hello.HelloMaven();
		assertEquals("Hello Maven", result);
	}
}

以上就是一个典型单元测试的三个步骤:

  • 准备测试类及数据
  • 执行要测试的行为
  • 检查结果

Junit3中,约定需要执行的测试方法以test开头,Junit4仍然推荐遵循这一规定。另外,Junit4中,需要执行的测试方法都应该以@Test进行标注。

测试用例编写完成后,可以执行测试了。如果是通过mvn命令行的话,那需要运行mvn clean test

打包和运行

打包mvn clean package

输出的日志里可以看到jar:jar的形式,实际上就是把jar插件的jar目标将项目主代码进行打包。

如果想让其他的Maven项目直接引用这个jar包作为依赖的话,则需要把这个jar包安装到本地仓库:

安装mvn clean install

这里需要注意,默认打包生成的jar是不能够直接运行的。如果直接java -jar运行的话,会提示jar包中没有主清单属性

这是因为带有main方法的入口类不会添加到manifest中,即打开jar文件中的META-INF/MANIFEST.MF文件,无法看到Main-Class一行。

另外,如果你的项目里有依赖其他jar包的话,Maven默认的package也不会把它们打进去,而只会打包自身的class文件。

那如果想生成可执行的jar文件,该怎么办呢?

两种解决方案:

  1. 给上一步生成的jar包添加主清单属性,即在MANIFEST.MF添加一行:Main-Class: org.wlh.helloworld.Hello,即入口类的完整路径,再java -jar运行就可以了。
  2. 借助其他插件,比如说maven-shade-plugin插件,一步到位生成可执行jar包。

常用打包方式

在Maven中,其实提供了3种方式来打包:

  • maven-jar-plugin:默认的打包插件,用来打普通的jar包,不可执行;
  • maven-shade-plugin:用来打可执行jar包,就是所谓的fat jar包,也支持依赖项目过滤等;
  • maven-assembly-plugin:支持自定义的打包结构,也可以定制依赖项目等。

maven-jar-plugin打的包,只包含了项目本身的class文件,并没有把项目依赖的所有jar包一起打进去,再加上默认不会指定main-class,所以这样打出来的包不能执行。

maven-shade-plugin和maven-assembly-plugin打出来的包都是含项目依赖的所有jar包的,所以都是可以执行的。二者的具体区别在于,maven-assembly-plugin的一项bug

一个项目一般会依赖很多jar包,而这些被依赖的jar包可能又会依赖其他的jar包,这样的话,项目本身可能会依赖不同版本的相同包(假设是spring包)。

在这种前提下,使用assembly打包的时候,只能将某一个版本的spring.schemas文件放入最终打出的jar包里,这样会有问题。

而使用shade进行打包的时候,它能够将所有spring jar包中的spring.schemas文件进行合并,所以最终生成的jar包里,相当于是会包含项目中所有出现过的spring版本。

基于这种问题,如果只是想打可执行包的话,推荐使用maven-shade-plugin就可以了

2022-5-17 16:22:46 但是好像要配置一下?这个看看参考文献3吧。

下面讲一下如何通过maven-shade-plugin插件来生成可执行文件:

maven-shade-plugin

使用

在pom.xml文件中加入下列代码:

记得把mainClass节点里的路径换成你项目的入口类就可以。

<project>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>3.0.0</version>
				<configuration>
					<createDependencyReducedPom>false</createDependencyReducedPom>
				</configuration>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
						<configuration>
							<transformers>
								<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
									<mainClass>org.wlh.helloworld.Hello</mainClass>
								</transformer>
							</transformers>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

基于shade插件打包完毕后,target目录下会生成两个jar包,一个是origin-xxx.jar,一个是xxx.jar。其中origin-xxx.jar中只包含了工程自己的class文件,而xxx.jar包含了项目本身的class + 所依赖的jar包。用第二个就可以。

而且也可以看到,xxx.jar中包含了Main-Class一行。

filter与artifactSet - 过滤jar包

利用filter可以在打包的时候排除掉jar包中的部分内容。

以 groupId:artifactId 为标识,在 filter 内部可以使用<include>/<exclude>更细致地控制,既可以移除代码文件,也可以移除配置文件。

<!-- 按package过滤junit包 -->
<configuration>
    <filters>
        <filter>
        	<artifact>junit:junit</artifact>
        	<includes>
        		<include>junit/framework/**</include>
        		<include>org/junit/**</include>
        	</includes>
        	<excludes>
        		<exclude>org/junit/experimental/**</exclude>
        		<exclude>org/junit/runners/**</exclude>
        	</excludes>
        </filter>
    </filters>
</configuration>

如果想将整个 jar 包都过滤掉,可以使用<artifactSet>,也是指定 groupId:artifactId 的标识。

<configuration>
	<artifactSet>
		<excludes>
			<exclude>classworlds:classworlds</exclude>
			<exclude>junit:junit</exclude>
			<exclude>jmock:*</exclude>
			<exclude>*:xml-apis</exclude>
			<exclude>org.apache.maven:lib:tests</exclude>
			<exclude>log4j:log4j:jar:</exclude>
		</excludes>
	</artifactSet>
</configuration>

另外配置<minimizeJar>将项目中没有使用的依赖自动移除

<configuration>
    <minimizeJar>true</minimizeJar>
</configuration>

Transformer

shade插件提供了丰富的Transformer工具类,这里介绍一些常用的Transformer。更多的 Transformer 见http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html

ManifestResourceTransformer

作用是往 MANIFEST 文件中写入 Main-Class ,这是可执行包的必要条件。

<configuration>
	<transformers>
		<transformer 
			implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
			<mainClass>com.lcifn.Application</mainClass>
		</transformer>
	</transformers>
</configuration>

AppendingTransformer

用来处理多个jar包中存在相同的配置文件的合并,尤其是spring。

<configuration>
	<transformers>
		<transformer
			implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
			<resource>META-INF/spring.handlers</resource>
		</transformer>
		<transformer
			implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
			<resource>META-INF/spring.schemas</resource>
		</transformer>
	</transformers>
</configuration>

ServicesResourceTransformer

JDK 的服务发现机制是基于 META-INF/services/目录的,如果同一接口存在多个实现需要合并 ,则可以使用此 Transformer。

<configuration>
  <transformers>
    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
  </transformers>
</configuration>

参考文献

  1. 通过IDEA 快速 生成 可执行 jar包(超级简单) 没成功,但是过程有点意思。
  2. Maven3种打包方式之一maven-shade-plugin的使用
  3. [1119使用maven插件maven-shade-plugin对可执行java工程及其全部依赖jar进行打包

猜你喜欢

转载自blog.csdn.net/wlh2220133699/article/details/131291494