如何使用已解压的依赖运行Spring Boot应用?
我有一个可启动HTTP服务器的简单Spring Boot应用:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
我通过unpack-dependencies目标编译该应用,并将所有依赖复制到classes文件夹中:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.1</version> <executions> <execution> <id>unpack-dependencies</id> <goals> <goal>unpack-dependencies</goal> </goals> <configuration> <excludes> module-info.class </excludes> <outputDirectory>${project.build.outputDirectory}</outputDirectory> </configuration> </execution> </executions> </plugin>
之后我尝试不构建jar文件直接运行应用:
java -cp "target/classes" my.Application
但使用该命令启动时,Spring Boot似乎无法加载所需依赖,应用启动后立即结束,并未启动HTTP服务器。
若执行以下命令:
mvn spring-boot:run
应用可成功启动。我查看日志发现,使用该命令时Spring Boot会使用.m2文件夹中的依赖,而非classes文件夹内的已解压依赖。
请问能否不构建jar包,使用已解压的依赖启动Spring Boot应用?我在指定类路径时遗漏了什么?
可以直接用解压后的依赖启动应用,问题出在Spring Boot的自动配置与类加载机制需要特定的类路径结构,以及你当前的类路径设置存在缺陷。
1. 当前启动命令失败的原因
你将所有依赖解压到target/classes目录下,导致应用自身的类文件与依赖的类文件完全混杂,Spring Boot无法正确区分应用类和依赖类,进而无法触发自动配置逻辑(比如Web服务器的启动流程)。此外,部分Spring Boot核心依赖包含的配置文件、资源文件也会因目录结构混乱而无法被正确加载。
2. 正确的实现方式
不要将依赖解压到target/classes,而是单独解压到一个独立目录(比如target/dependency),再将应用类目录和依赖目录完整加入类路径。
步骤1:修改Maven插件配置,指定独立的依赖解压目录
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.1</version> <executions> <execution> <id>unpack-dependencies</id> <goals> <goal>unpack-dependencies</goal> </goals> <configuration> <excludes> module-info.class </excludes> <!-- 将依赖单独解压到target/dependency目录 --> <outputDirectory>${project.build.directory}/dependency</outputDirectory> </configuration> </execution> </executions> </plugin>
步骤2:执行Maven命令完成编译与依赖解压
mvn clean compile dependency:unpack-dependencies
步骤3:构造完整类路径启动应用
- Windows系统:
java -cp "target/classes;target/dependency/*" my.Application
- Linux/macOS系统:
java -cp "target/classes:target/dependency/*" my.Application
3. 额外说明
如果坚持要将依赖解压到target/classes,需要确保Spring Boot的自动配置类能被正确扫描,但这种方式极易引发类冲突(比如不同依赖的同名类互相覆盖),不推荐使用。
另外,mvn spring-boot:run之所以能成功启动,是因为它自动构建了包含所有依赖的完整类路径,并且使用Spring Boot专用类加载器处理应用类与依赖类的隔离。
内容的提问来源于stack exchange,提问作者Volodya Lombrozo

