Spring Boot2 + Gradle5 + Log4j2 启动出现 StackOverflowError 问题的解决方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot2 + Gradle5 + Log4j2 启动出现 StackOverflowError 问题的解决方法相关的知识,希望对你有一定的参考价值。
参考技术A在 build.gradle 中配置好 log4j2 后,bootRun 启动项目,抛出以下异常:
原因 是新引入的 log4j2 与 spring-boot-starter-logging 模块中的 log4j 冲突了:
解决: 在 build.gradle 中,排除 spring-boot-starter-logging 即可:
飞舞的spring boot2(52)——Spring Boot Gradle插件
Spring Boot Gradle插件
Spring Boot Gradle插件为Gradle提供Spring Boot支持,它允许你打包可执行jar或war存档,运行Spring Boot应用,使用spring-boot-dependencies提供的依赖管理。以下为参考官方文档:
Reference (HTML and PDF)
API
包含该插件
想要使用Spring Boot Gradle插件,你只需简单的包含一个buildscript依赖,并应用spring-boot插件:
buildscript { dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.1.RELEASE") } } apply plugin: 'spring-boot'
如果使用的是一个里程碑或快照版本,你需要添加相应的repositories引用:
buildscript { repositories { maven.url "http://repo.spring.io/snapshot" maven.url "http://repo.spring.io/milestone" } // ... }
Gradle依赖管理
spring-boot插件自动应用Dependency Management Plugin,并配置它导入spring-boot-starter-parentbom。这提供了跟Maven用户喜欢的相似依赖管理体验,例如,如果声明的依赖在bom中被管理的话,你就可以省略版本。为了充分使用该功能,只需要想通常那样声明依赖,但将版本号设置为空:
dependencies { compile("org.springframework.boot:spring-boot-starter-web") compile("org.thymeleaf:thymeleaf-spring4") compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect") }
注 你声明的spring-boot Gradle插件的版本决定了spring-boot-starter-parent bom导入的版本(确保可以重复构建)。你最好将spring-boot gradle插件版本跟Spring Boot版本保持一致,版本详细信息可以在附录中查看。
spring-boot插件对于没有指定版本的依赖只会提供一个版本。如果不想使用插件提供的版本,你可以像平常那样在声明依赖的时候指定版本。例如:
dependencies { compile("org.thymeleaf:thymeleaf-spring4:2.1.1.RELEASE") }
打包可执行jar和war文件
一旦spring-boot插件被应用到你的项目,它将使用bootRepackage任务自动尝试重写存档以使它们能够执行。为了构建一个jar或war,你需要按通常的方式配置项目。
你想启动的main类既可以通过一个配置选项指定,也可以通过向manifest添加一个Main-Class属性。如果你没有指定main类,该插件会搜索带有public static void main(String[] args)方法的类。
为了构建和运行一个项目artifact,你可以输入以下内容:
$ gradle build $ java -jar build/libs/mymodule-0.0.1-SNAPSHOT.jar
为了构建一个即能执行也可以部署到外部容器的war包,你需要将内嵌容器依赖标记为providedRuntime,比如:
... apply plugin: 'war' war { baseName = 'myapp' version = '0.5.0' } repositories { jcenter() maven { url "http://repo.spring.io/libs-snapshot" } } configurations { providedRuntime } dependencies { compile("org.springframework.boot:spring-boot-starter-web") providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") ... }
注 具体参考“Section 81.1, “Create a deployable war file””。
就地(in-place)运行项目
为了在不先构建jar的情况下运行项目,你可以使用bootRun任务:
$ gradle bootRun
如果项目中添加了devtools,它将自动监控你的应用变动。此外,你可以运行应用,这样静态classpath资源(比如,默认位于src/main/resources下)在应用运行期间将能够重新加载,这在开发期间是非常有用的:
bootRun { addResources = true }
让静态classpath资源可加载意味着bootRun不使用processResources任务的输出,例如,当使用bootRun调用时,你的应用将以未经处理的形式使用资源。
Spring Boot插件配置
Gradle插件自动扩展你的构建脚本DSL,它为脚本添加一个springBoot元素以此作为Boot插件的全局配置。你可以像配置其他Gradle扩展那样为springBoot设置相应的属性(下面有配置选项列表)。
springBoot { backupSource = false }
Repackage配置
该插件添加了一个bootRepackage任务,你可以直接配置它,比如:
bootRepackage { mainClass = 'demo.Application' }
下面是可用的配置选项:
名称 | 描述 |
---|---|
enabled | 布尔值,用于控制repackager的开关(如果你只想要Boot的其他特性而不是这个,那它就派上用场了) |
mainClass | 要运行的main类。如果没有指定,则使用project属性mainClassName。如果该应用插件没有使用或没有定义mainClassName,则搜索存档以寻找一个合适的类。"合适"意味着一个唯一的,具有良好格式的main()方法的类(如果找到多个则构建会失败)。你也可以通过run任务(main属性)指定main类的名称,和/或将"startScripts"(mainClassName属性)作为"springBoot"配置的替代。 |
classifier | 添加到存档的一个文件名字段(在扩展之前),这样最初保存的存档仍旧存放在最初的位置。在存档被重新打包(repackage)的情况下,该属性默认为null。默认值适用于多数情况,但如果你想在另一个项目中使用原jar作为依赖,最好使用一个扩展来定义该可执行jar |
withJarTask | Jar任务的名称或值,用于定位要被repackage的存档 |
customConfiguration | 自定义配置的名称,用于填充内嵌的lib目录(不指定该属性,你将获取所有编译和运行时依赖) |
executable | 布尔值标识,表示jar文件在类Unix系统上是否完整可执行,默认为false |
embeddedLaunchScript | 如果jar是完整可执行的,该内嵌启动脚本将添加到jar。如果没有指定,将使用Spring Boot默认的脚本 |
embeddedLaunchScriptProperties | 启动脚本暴露的其他属性,默认脚本支持mode属性,值可以是auto,service或run |
excludeDevtools | 布尔值标识,表示devtools jar是否应该从重新打包的存档中排除出去,默认为false |
使用Gradle自定义配置进行Repackage
有时候不打包解析自compile,runtime和provided作用域的默认依赖可能更合适些。如果创建的可执行jar被原样运行,你需要将所有的依赖内嵌进该jar中;然而,如果目的是explode一个jar文件,并手动运行main类,你可能在CLASSPATH下已经有一些可用的库了。在这种情况下,你可以使用不同的依赖集重新打包(repackage)你的jar。
使用自定义的配置将自动禁用来自compile,runtime和provided作用域的依赖解析。自定义配置即可以定义为全局的(处于springBoot部分内),也可以定义为任务级的。
task clientJar(type: Jar) { appendix = 'client' from sourceSets.main.output exclude('**/*Something*') } task clientBoot(type: BootRepackage, dependsOn: clientJar) { withJarTask = clientJar customConfiguration = "mycustomconfiguration" }
在以上示例中,我们创建了一个新的clientJar Jar任务从你编译后的源中打包一个自定义文件集。然后我们创建一个新的clientBoot BootRepackage任务,并让它使用clientJar任务和mycustomconfiguration。
configurations { mycustomconfiguration.exclude group: 'log4j' } dependencies { mycustomconfiguration configurations.runtime }
在BootRepackage中引用的配置是一个正常的Gradle配置。在以上示例中,我们创建了一个新的名叫mycustomconfiguration的配置,指示它来自一个runtime,并排除对log4j的依赖。如果clientBoot任务被执行,重新打包的jar将含有所有来自runtime作用域的依赖,除了log4j jars。
配置选项
可用的配置选项如下:
名称 | 描述 |
---|---|
mainClass | 可执行jar运行的main类 |
providedConfiguration | provided配置的名称(默认为providedRuntime) |
backupSource | 在重新打包之前,原先的存档是否备份(默认为true) |
customConfiguration | 自定义配置的名称 |
layout | 存档类型,对应于内部依赖是如何制定的(默认基于存档类型进行推测),具体查看available layouts |
requiresUnpack | 一个依赖列表(格式为"groupId:artifactId",为了运行,它们需要从fat jars中解压出来。)所有节点被打包进胖jar,但运行的时候它们将被自动解压 |
可用的layouts
layout属性用于配置存档格式及启动加载器是否包含,以下为可用的layouts:
名称 | 描述 | 可执行 |
---|---|---|
JAR | 常规的可执行JAR layout | 是 |
WAR | 可执行WAR layout,provided依赖放置到WEB-INF/lib-provided,以免war部署到servlet容器时造成冲突 | 是 |
ZIP(别名DIR) | 跟JAR layout类似,使用PropertiesLauncher | 是 |
MODULE | 捆绑(Bundle)依赖(排除那些provided作用域的依赖)和项目资源 | 否 |
NONE | 捆绑(Bundle)所有依赖和项目资源 | 否 |
理解Gradle插件是如何工作的
当spring-boot应用到你的Gradle项目,一个默认的名叫bootRepackage的任务被自动创建。bootRepackage任务依赖于Gradle assemble任务,当执行时,它会尝试找到所有限定符为空的jar artifacts(也就是说,tests和sources jars被自动跳过)。
由于bootRepackage会查找'所有'创建的jar artifacts,Gradle任务执行的顺序就非常重要了。多数项目只创建一个单一的jar文件,所以通常这不是一个问题。然而,如果你正打算创建一个更复杂的,使用自定义jar和BootRepackage任务的项目setup,有几个方面需要考虑。
如果'仅仅'从项目创建自定义jar文件,你可以简单地禁用默认的jar和bootRepackage任务:
jar.enabled = false bootRepackage.enabled = false
另一个选项是指示默认的bootRepackage任务只能使用一个默认的jar任务:
bootRepackage.withJarTask = jar
如果你有一个默认的项目setup,在该项目中,主(main)jar文件被创建和重新打包。并且,你仍旧想创建额外的自定义jars,你可以将自定义的repackage任务结合起来,然后使用dependsOn,这样bootJars任务就会在默认的bootRepackage任务执行以后运行:
task bootJars bootJars.dependsOn = [clientBoot1,clientBoot2,clientBoot3] build.dependsOn(bootJars)
上面所有方面经常用于避免一个已经创建的boot jar又被重新打包的情况。重新打包一个存在的boot jar不是什么大问题,但你可能会发现它包含不必要的依赖。
使用Gradle将artifacts发布到Maven仓库
如果声明依赖但没有指定版本,且想要将artifacts发布到一个Maven仓库,那你需要使用详细的Spring Boot依赖管理来配置Maven发布。通过配置它发布继承自spring-boot-starter-parent的poms或引入来自spring-boot-dependencies的依赖管理可以实现该需求。这种配置的具体细节取决于你如何使用Gradle及如何发布该artifacts。
自定义Gradle,用于产生一个继承依赖管理的pom
下面示例展示了如何配置Gradle去产生一个继承自spring-boot-starter-parent的pom,更多信息请参考Gradle用户指南。
uploadArchives { repositories { mavenDeployer { pom { project { parent { groupId "org.springframework.boot" artifactId "spring-boot-starter-parent" version "1.4.1.RELEASE" } } } } } }
自定义Gradle,用于产生一个导入依赖管理的pom
以下示例展示了如何配置Gradle产生一个导入spring-boot-dependencies提供的依赖管理的pom,更多信息请参考Gradle用户指南。
uploadArchives { repositories { mavenDeployer { pom { project { dependencyManagement { dependencies { dependency { groupId "org.springframework.boot" artifactId "spring-boot-dependencies" version "1.4.1.RELEASE" type "pom" scope "import" } } } } } } } }
以上是关于Spring Boot2 + Gradle5 + Log4j2 启动出现 StackOverflowError 问题的解决方法的主要内容,如果未能解决你的问题,请参考以下文章