写这篇文章的主要原因是出了这个bug,觉得很有意思,就研究了一下。
在用Maven之前,为了往本地项目里导入外部依赖,我一直是从各个依赖的官网直接下载jar包,然后手动添加进项目的lib文件夹里。现在用了Maven,但我为了省事(虽然最后事与愿违),就想着能不能还像之前一样直接从本地的jar包添加依赖,让这些jar包能跟着项目走,就踩了这次的坑。
应该都知道怎么从本地导入jar包,就不再赘述了。Eclipse和IDEA的操作方式稍微有点区别,不过问题不大。
Maven的原理,大概是有一个远程仓库和本地仓库,以及一个配置文件pom.xml
,用的时候把pom.xml
中定义的jar包从远程仓库下载到本地仓库,然后只需要简单配置一下,就可以使用项目需要的依赖,不用再一个一个手动导入,而且比手动导入优雅很多。
导入外部依赖最简单的办法是从远程仓库直接导入,语法也很简单:
<dependencies>
<dependency>
<groupId>example</groupId>
<artifactId>example</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
但这样做的问题也很明显:万一远程仓库里没有怎么办?即使有,也面临着下载慢(外网),和下载不稳定的问题。一旦本地仓库出现偏差,或者Maven升级就需要重新配置。再者,在多人协作的时候,每到一个人手里就得往本地仓库里下载一遍,而且每个人都会面临,并且重复之前所说的问题。
不过,用远程仓库虽然有各种各样可能存在的问题,但是一般远程仓库有还是用远程仓库,最主要的原因就是配置方便,而这也是Maven出现的初衷吧;不然手动配置会很麻烦,得不偿失。
用这种本地依赖的一个例子是配置SqlServer的依赖,因为Microsoft官方不提供Maven支持,所以只能下载jar包然后添加本地依赖。至于导入本地的jar包,这次主要参考了这篇文章,是老外写的,里面介绍了三种办法,我自己是用方法一(install插件+dependency插件)和方法二(System Scope)的。方法三是把项目里的lib文件夹建成一个额外的Maven本地仓库,但是相对费事一点(而且对于这种少量的jar包来说,可以用,但没必要),主要还是介绍方法一和二。先上配置文件(方法一),有点长:
<?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>example</groupId>
<artifactId>example</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc6</artifactId>
<version>6.0.8112.200</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>install-javax-mail-jar</id>
<phase>clean</phase>
<configuration>
<repositoryLayout>default</repositoryLayout>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.6.2</version>
<file>${basedir}/src/lib/javax.mail.jar</file>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
<execution>
<id>install-activation-jar</id>
<phase>clean</phase>
<configuration>
<repositoryLayout>default</repositoryLayout>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<file>${basedir}/src/lib/activation.jar</file>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
<execution>
<id>install-jdbc-jar</id>
<phase>clean</phase>
<configuration>
<repositoryLayout>default</repositoryLayout>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc6</artifactId>
<version>6.0.8112.200</version>
<file>${basedir}/src/lib/sqljdbc42.jar</file>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>ui.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
方法一是很正统的做法。具体过程是,通过install插件,把项目里lib/
文件夹下的jar包安装到本地仓库里,然后再用dependency插件从仓库里“搬”到生成的target文件夹的指定目录里(一般是target/lib
)。
方法二有点取巧,就是直接把<dependency>
的来源换成本地指定目录(原来是远程仓库找,现在直接在本地找)。这个方法明显比方法一三都方便,如果还比另外两个方法稳定,也就没有另外两个方法了。这个方法的主要缺陷,是只能应对jar包;如果要打包成war包,就无能为力了。(而且据我测试,target/
里生成的jar包也会出现在其他位置,可能是根目录,不像方法一一样出现在指定的地方,所以我又用了dependency插件去移位)
值得一提的是,从本地导入的jar包,如果没有手动添加到项目依赖里,在编译期可能会找不到依赖导致无法编译;所以为了确保能够编译,还需要一个compile插件。
另外,这里用的jar插件,是为了指定manifest.mf
里面的classpath,让jar文件在运行的时候能够找到依赖来执行。Classpath是相对于jar包所在的位置而言的,所以是直接是lib/
,而不是target/lib/
。
如果要直接生成可执行的jar包(而不是需要外部依赖的那种),可以直接用shade插件,简单方便。