需要一个小小的算法,在 GitHub 上找到一个相关的原型工程,拉下来后发现要把它跑起来有点困难。代码距今已经达八年之久,工程文件是 .project
,找了一下信息后基本可以确定,如果你有 Eclipse+JDT,应该就是可以直接打开的,然而我没有。我只有一个百纳环境来处理这些非日常工程,那就是 VSCode,可是它里面集成的插件只认 maven 构建所需的 pom.xml
。
于是开始寻找如何从 .project
迁移到 pom.xml
。找到的方案大多令人啼笑皆非,说 Eclipse 下有个插件可以做到这件事,可问题是我如果有个 Eclipse 那就不需要做这件事了。最后决定查阅这两种工程之间的具体差别,手工完成 pom.xml
。
神奇的是,maven 的工程好像也还需要 .project
文件的存在,只是要补充一些内容进去。原始文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>ContourTracer</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> |
添入 maven 所需内容后如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>ContourTracer</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>org.eclipse.m2e.core.maven2Builder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature> </natures> </projectDescription> |
第一步结束。接下来要手工打造 pom.xml
了,先构造一个框架,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<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>com.helloworld</groupId> <artifactId>contourtracer</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>ContourTracer</name> <url>http://www.contourtracer.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project> |
其中,从 groupId
到 url
根据工程自身的信息填写就好了。接下来要在 properties
下增加两个节点,dependencies
和 build
。在实际的操作中,前者里的明细条目可能是需要把后者基本完善到能够执行构建过程后才能一一补充完整的。最终的 pom.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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<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>com.helloworld</groupId> <artifactId>contourtracer</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>ContourTracer</name> <url>http://www.contourtracer.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-swing</artifactId> <version>1.14</version> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-dom</artifactId> <version>1.14</version> </dependency> </dependencies> <build> <sourceDirectory>src/</sourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>tracer.main.MainWindow</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> |
build
节点下的 plugins
下的三个 plugin
,一个是编译器,一个是打包器,一个是依赖包处理器。最重要的是,我一开始漏掉了 sourceDirectory
,导致打出来的 jar
文件里没有任何代码。你一次次地尝试编译,根据出错信息就可以把 dependencies
节点下的各个 dependency
列举完了。我的实际经历还要更悲催一些,因为代码太老,有些依赖的包名都发生了变化,不过最终还是搞定了。
打包并不止一种方式,上面的方式是把依赖包复制到主包所在路径下,还有一种把所有依赖都打进最终单一包体内的方法。以下所附文章内容中说的很详细。
Maven 生成可以直接运行的 jar 包的多种方式
Maven 可以使用 mvn package
指令对项目进行打包,如果使用 java -jar xxx.jar
执行运行 jar
文件,会出现“no main manifest attribute, in xxx.jar
”(没有设置 Main-Class
)、ClassNotFoundException
(找不到依赖包)等错误。
要想 jar
包能直接通过 java -jar xxx.jar
运行,需要满足:
1、在 jar
包中的 META-INF/MANIFEST.MF
中指定 Main-Class
,这样才能确定程序的入口在哪里;
2、要能加载到依赖包。
使用 Maven 有以下几种方法可以生成能直接运行的 jar
包,可以根据需要选择一种合适的方法。
方法一:使用 maven-jar-plugin 和 maven-dependency-plugin 插件打包
在 pom.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 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.6</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.xxg.Main</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> |
maven-jar-plugin 用于生成 META-INF/MANIFEST.MF
文件的部分内容,<mainClass>com.xxg.Main</mainClass>
指定 MANIFEST.MF
中的 Main-Class
,<addClasspath>true</addClasspath>
会在 MANIFEST.MF
加上 Class-Path
项并配置依赖包,<classpathPrefix>lib/</classpathPrefix>
指定依赖包所在目录。
例如下面是一个通过 maven-jar-plugin 插件生成的 MANIFEST.MF
文件片段:
1 2 |
Class-Path: lib/commons-logging-1.2.jar lib/commons-io-2.4.jar Main-Class: com.xxg.Main |
只是生成 MANIFEST.MF
文件还不够,maven-dependency-plugin 插件用于将依赖包拷贝到 <outputDirectory>${project.build.directory}/lib</outputDirectory>
指定的位置,即 lib
目录下。
配置完成后,通过 mvn package
指令打包,会在 target
目录下生成 jar
包,并将依赖包拷贝到 target/lib
目录下,目录结构如下:
1 2 3 4 5 |
`-+- target +- lib | +- commons-io-2.4.jar | +- commons-logging-1.2.jar +- test.jar |
指定了 Main-Class
,有了依赖包,那么就可以直接通过 java -jar xxx.jar
运行 jar
包了。
这种方式生成 jar
包有个缺点,就是生成的 jar
包太多不便于管理,下面两种方式只生成一个 jar
文件,包含项目本身的代码、资源以及所有的依赖包。
方法二:使用 maven-assembly-plugin 插件打包
在 pom.xml
中配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.5.5</version> <configuration> <archive> <manifest> <mainClass>com.xxg.Main</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build> |
打包方式:
1 |
mvn package assembly:single |
打包后会在 target
目录下生成一个 xxx-jar-with-dependencies.jar
文件,这个文件不但包含了自己项目中的代码和资源,还包含了所有依赖包的内容。所以可以直接通过 java -jar
来运行。
此外还可以直接通过 mvn package
来打包,无需 assembly:single
,不过需要加上一些配置:
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 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.5.5</version> <configuration> <archive> <manifest> <mainClass>com.xxg.Main</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
其中 <phase>package</phase>
、<goal>single</goal>
即表示在执行 package 打包时,执行 assembly:single
,所以可以直接使用 mvn package
打包。
不过,如果项目中用到 Spring Framework,用这种方式打出来的包运行时会出错,使用下面的方法三可以处理。
方法三:使用 maven-shade-plugin 插件打包
在 pom.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 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.xxg.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> |
配置完成后,执行 mvn package
即可打包。在 target
目录下会生成两个 jar
包,注意不是 original-xxx.jar
文件,而是另外一个。和 maven-assembly-plugin 一样,生成的 jar
文件包含了所有依赖,所以可以直接运行。
如果项目中用到了 Spring Framework,将依赖打到一个 jar
包中,运行时会出现读取 XML schema 文件出错。原因是 Spring Framework 的多个 jar
包中包含相同的文件 spring.handlers
和 spring.schemas
,如果生成一个 jar
包会互相覆盖。为了避免互相影响,可以使用 AppendingTransformer 来对文件内容追加合并:
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 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.xxg.Main</mainClass> </transformer> <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> </execution> </executions> </plugin> </plugins> </build> |
另外还有一篇文章详述 pom.xml
中的各个节点的含义,实在太长,就不全文引入了:https://www.cnblogs.com/hafiz/p/5360195.html