文章目录
- 使用入门
- 项目骨架
- pom.xml
- 构建过程
- 编写测试
- 打包和运行
- 常用打包方式
- maven-shade-plugin
- 使用
- filter与artifactSet - 过滤jar包
- Transformer
- 参考文献
使用入门
项目骨架
对于一个使用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/java
,src/test/java
,pom.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文件,该怎么办呢?
两种解决方案:
- 给上一步生成的jar包添加主清单属性,即在MANIFEST.MF添加一行:
Main-Class: org.wlh.helloworld.Hello
,即入口类的完整路径,再java -jar运行就可以了。 - 借助其他插件,比如说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>
参考文献
- 通过IDEA 快速 生成 可执行 jar包(超级简单) 没成功,但是过程有点意思。
- Maven3种打包方式之一maven-shade-plugin的使用
- [1119使用maven插件maven-shade-plugin对可执行java工程及其全部依赖jar进行打包