问题引出
在配置Spring Boot程序时,build.gradle中配置:
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
虽使用./gradlew bootRun
运行一切正常,但在Idea中运行程序直接退出而不是启动服务端程序。
而在将providedRuntime修改为compile,idea运行正常。
问题合理解决的另一个方式,修改为providedRuntime('org.springframework.boot:spring-boot-starter-tomcat@jar')
。
但是这后面隐藏着什么样的知识呢,到底什么是compile,什么是providedRuntime?
依赖配置
依赖管理主要包括两部分:一是依赖,即项目构建或运行时所需要的一些文件;二是发布,即构建完成后上传到某个地方。本文只记录了依赖配置部分。
在build.gradle中配置项目所用到的依赖,gradle可以根据配置的依赖从远程的Maven,Ivy库或本地目录中下载依赖。依赖在build.gradle中配置的例子。
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compileOnly('org.springframework.boot:spring-boot-starter-tomcat')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Idea的java插件,Java插件定义了一些标准配置(compile, runtime, testCompile, testRuntime),这些配置一起形成了插件本身的类路径库,其都有着不同的含义:
-
compile: 该依赖对于编译发行是必须的。
-
runtime: 该依赖对于运行时是必须的,默认包含编译时依赖。
-
testCompile: 该依赖对于编译测试是必须的,默认包含编译产品依赖和编译时依赖。
-
testRuntime: 该依赖对于测试是必须的,默认包含编译、运行时、测试编译依赖。
-
compileOnly: gradle 2.12后引入的。改依赖只用于编译器,不用于运行期。
依赖仓库
Gradle 是在一个被称之为仓库的地方找寻所需的外部依赖。仓库即是一个按 group,name 和 version 规则进行存储的一些文件。Gradle 可以支持不同的仓库存储格式,如 Maven 和 Ivy,并且还提供多种与仓库进行通信的方式,如通过本地文件系统或 HTTP。
默认情况下,Gradle 没有定义任何仓库,你需要在使用外部依赖之前至少定义一个仓库,例如 Maven 中央仓库。
repositories {
mavenCentral()
}
使用其他远程仓库:
repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
依赖配置与打包的关系
War插件添加了两个依赖配置: providedCompile和providedRuntime。这两个配置的与compile和runtime配置有相同作用于,不同的是是它们不会添加到WAR文件中。
providedCompile - Additional compile classpath for libraries that should not be part of the WAR archive.
providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR archive.
要特别注意的是,这些provided配置是有传递效应的。假设你添加commons-httpclient:commons-httpclient:3.0依赖到任何一个build.grale的provided配置。而commons-httpclient依赖于commons-codec。由于commons-httpclient是provided配置,它的所有依赖都不会被打包到war中(即便你在build.gradle中定义了compile …commons-codec…)。如果想去除这种传递效应,可以在provided依赖时加上@jar, 如 providedCompile commons-httpclient:commons-httpclient:3.0@jar.
或
providedCompile ('commons-httpclient:commons-httpclient:3.0@jar') {
transitive = false
}
在开篇时引起本文的问题就是使用@jar来解决问题,使用./gradlew dependencies
可以查看项目的依赖,在去掉@jar时。providedRuntime的依赖被解析成:
providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR archive.
\--- org.springframework.boot:spring-boot-starter-tomcat: -> 1.5.1.RELEASE
+--- org.apache.tomcat.embed:tomcat-embed-core:8.5.11
+--- org.apache.tomcat.embed:tomcat-embed-el:8.5.11
\--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.11
\--- org.apache.tomcat.embed:tomcat-embed-core:8.5.11
在有@jar时,providedRuntime的依赖被解析成:
providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR archive.
\--- org.springframework.boot:spring-boot-starter-tomcat: -> 1.5.1.RELEASE
我们再对比在初始./gradlew build
后生成的war的差别。
去掉@jar的情况,tar tvf build/libs/demo-0.0.1-SNAPSHOT.war
:
WEB-INF/lib-provided/spring-boot-starter-tomcat-1.5.1.RELEASE.jar
WEB-INF/lib-provided/tomcat-embed-core-8.5.11.jar
WEB-INF/lib-provided/tomcat-embed-el-8.5.11.jar
WEB-INF/lib-provided/tomcat-embed-websocket-8.5.11.jar
有@jar的情况
WEB-INF/lib/tomcat-embed-core-8.5.11.jar
WEB-INF/lib/tomcat-embed-el-8.5.11.jar
WEB-INF/lib/tomcat-embed-websocket-8.5.11.jar
WEB-INF/lib-provided/spring-boot-starter-tomcat-1.5.1.RELEASE.jar
现在还剩下一个问题没有解释清楚,就是为什么./gradlew bootRun
能在没有@jar的时候运行正常,查看其log,发现:
Application started with classpath: [.....gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-core/8.5.11/72761f51fc7cef3ee19d4aafc7adc605df9f611f/tomcat-embed-core-8.5.11.jar ......]
其将tomcat启动需要的库都加入到了程序运行时的classpath中,自然也就能正常启动tomcat了。
以上内容也是本人的理解,如有谬误,请自行屏蔽上述内容
另测试了一下使用compileOnly,打包后发现tomcat相关的包都还在WEB-INF/lib下,具体问题查看compileOnly not useable for excluding jars from WAR?
自定义配置
providedCompile是war插件提供的,如果在打jar包时也希望不打包指定的依赖?gradle 2.12版本提供的compileOnly很好的解决了这个问题。如果想自己定义一个配置来满足这个需求呢?
configurations { providedCompile }
dependencies {
providedCompile "javax.servlet:javax.servlet-api:3.+"
}
sourceSets.main.compileClasspath += configurations.providedCompile
sourceSets.test.compileClasspath += configurations.providedCompile
sourceSets.test.runtimeClasspath += configurations.providedCompile
在sourceSets.main.runtimeClasspath并没有将其提供在其中,所以运行期不会提供此库。次解决方案貌似eclipse上有问题。