非Spring Java应用Slf4j+Log4j2打包Jar后报ClassNotFoundException
问题描述
这是一个不含Spring的简单Java应用,带有main方法。使用Lombok的@Slf4j注解,配合log4j2.xml配置文件。Gradle构建文件内容如下:
dependencies { ... implementation 'org.slf4j:slf4j-api:2.0.7' implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0' ... } configurations { testImplementation.extendsFrom compileOnly // excludes transitive dependencies implementation.transitive = false } jar { dependsOn check duplicatesStrategy = DuplicatesStrategy.FAIL configurations.implementation.setCanBeResolved(true) into('META-INF/lib') { from (configurations.implementation) } manifest { attributes("Spring-Context": "*;publish-context:=false") attributes( 'Class-Path': configurations.runtimeClasspath.files.collect { it.getName() }.join(' '), 'Main-Class': 'com.example.Application' ) } }
在IntelliJ中运行时日志功能正常,但执行可执行Jar文件时出现以下异常:
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory at dia.nmec.twm.s3_client_plugin.S3ClientPlugin.<clinit>(S3ClientPlugin.java:10) Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ... 1 more
解决建议
方法一:打包包含所有依赖的胖Jar(推荐)
用Gradle的Shadow插件直接把所有依赖和应用代码打包到一个Jar中,彻底避免类路径问题:
- 在
build.gradle中添加Shadow插件:
plugins { id 'java' id 'com.github.johnrengelman.shadow' version '8.1.1' }
- 配置ShadowJar任务指定主类(若manifest已配置可省略):
shadowJar { manifest { attributes 'Main-Class': 'com.example.Application' } }
- 执行打包命令:
./gradlew shadowJar,生成的胖Jar在build/libs/目录下(通常命名为xxx-all.jar),直接运行这个Jar即可。
方法二:修复现有打包逻辑,外置依赖
若不想用胖Jar,调整配置让依赖外置并保证类路径正确:
- 移除全局关闭依赖传递的配置:删除
implementation.transitive = false,如果需要排除特定传递依赖,针对单个依赖配置exclude,不要全局禁用。 - 修改Jar任务和添加依赖复制任务:
jar { dependsOn check duplicatesStrategy = DuplicatesStrategy.FAIL manifest { attributes("Spring-Context": "*;publish-context:=false") attributes( // 给依赖路径添加lib/前缀,对应外置的lib目录 'Class-Path': configurations.runtimeClasspath.files.collect { "lib/" + it.getName() }.join(' '), 'Main-Class': 'com.example.Application' ) } } // 新增任务:把依赖复制到Jar所在目录的lib文件夹 task copyDependencies(type: Copy) { from configurations.runtimeClasspath into "$buildDir/libs/lib" } // 让Jar打包任务先完成依赖复制 jar.dependsOn copyDependencies
- 执行
./gradlew jar后,build/libs下会有应用Jar和lib目录,在该目录下执行java -jar your-app.jar即可。
方法三:检查依赖冲突
执行./gradlew dependencies查看依赖树,确认是否有其他依赖引入了冲突的SLF4J版本。如果有,通过exclude排除冲突依赖:
implementation('冲突的依赖坐标') { exclude group: 'org.slf4j', module: 'slf4j-api' }
内容的提问来源于stack exchange,提问作者emiles
相关产品推荐
相关产品推荐

