前言
我一直都觉得spring boot很难用。。因为不知道里面做了些什么。。
一个项目,一个程序,一点就能够运行的话,那么如果我要拿来做二次开发我就会觉得—坑爹。。什么都不知道呢。
这次遇到的一个问题是spring boot自带的日志问题。额,不知道是bug,坑还是直接个人水平问题了。下面来解决一下。
问题重现
一个spring boot项目,具体来说
就是xxl-conf-admin的本地搭建以及编译。
编译过后,会发现出现这个问题:
背景简介:
项目背景是多模块项目,如下图:
其中,根项目的构建build.gradle如下:
plugins {
id 'java'
}
group 'net.w2p'
version '1.0-SNAPSHOT'
/***所有项目共通***/
allprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'groovy'
ext{
/***常见或主要第三方依赖版本号定义 begin***/
globalSpringVersion = "5.1.4.RELEASE"
globalSpringDataJpaVersion ="2.1.2.RELEASE"
globalSpringBootVersion = '2.1.1.RELEASE'
globalFastJsonVersion="1.2.54"
globalMyBatisVersion="3.4.6"
globalGoogleGuavaVersion="27.0.1-jre"
globalDom4jVersion="1.6.1"
globalJavaMailVersion="1.4.7"
globalJsoupVersion="1.11.3" //--一个过滤html危险字符串api,用于web安全
globalQuartzVersion="2.3.0"
globalFlexmarkVersion="0.34.32" //--java对markdown语法的解释以及翻译api
globalPostgresqlJdbcDriverVersion="42.2.5"
globalQiniuSdkVersion="7.2.18"//--七牛上传下载客户端sdk
globalApacheAntVersion="1.10.5"
globalGoogleZXingVersion="3.3.3"
globalFastdfsClientVersion="1.27"
globalLog4jVersion="1.2.17"
globalSlf4jVersion="1.7.25"
globalRedisClientVersion="2.10.1"
globalFreemarkerVersion="2.3.28"
globalSpringBootStaterVersionOfMyBatis="1.3.2"
globalMysqlJdbcDriverVersion="5.1.40"
globalApacheCommonLang3Version="3.8.1"
/***常见或主要第三方依赖版本号定义 end***/
/****常见或者程序主要引用依赖定义 begin****/
//--这个是spring boot要直接compile进去的框架。
ref4SpringBoot=[
/***spring boot 相关依赖***/
"org.springframework.boot:spring-boot:$globalSpringBootVersion",
"org.springframework.boot:spring-boot-starter:$globalSpringBootVersion",
"org.springframework.boot:spring-boot-starter-web:$globalSpringBootVersion",
"org.springframework.boot:spring-boot-starter-freemarker:$globalSpringBootVersion",
"org.springframework.boot:spring-boot-devtools:$globalSpringBootVersion"
]
//--这个是spring boot要compileOnly的类库
ref4SpringBootProvided=[
"org.springframework.boot:spring-boot-dependencies:$globalSpringBootVersion",
]
//--这个是spring boot的测试框架,用testCompile导入
ref4SpringBootTest=[
"org.springframework.boot:spring-boot-starter-test:$globalSpringBootVersion"
]
//--spring框架api
ref4SpringFramework=[
"org.springframework:spring-web:$globalSpringVersion",
"org.springframework:spring-webmvc:$globalSpringVersion",
"org.springframework:spring-jdbc:$globalSpringVersion",
"org.springframework:spring-context-support:$globalSpringVersion",
"org.springframework.data:spring-data-jpa:$globalSpringDataJpaVersion",
"org.springframework:spring-test:$globalSpringVersion"
]
//--jsp&servlet等javaweb容器api,通常都用 compileOnly引用的。
ref4JspAndServletApi=[
"javax.servlet:javax.servlet-api:3.1.0",
"javax.servlet.jsp:jsp-api:2.2",
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1"
]
//--jstl等java web的tag标准api,引入的话要用compile
ref4Jstl=[
'taglibs:standard:1.1.2',
'jstl:jstl:1.2'
]
//--mybatis
ref4MyBatis=[
"org.mybatis:mybatis:$globalMyBatisVersion"
]
//--这是apache common 类库引用的地址
ref4ApacheCommons = [
'commons-lang:commons-lang:2.6',
'commons-logging:commons-logging:1.2',
'commons-io:commons-io:2.5',
'commons-fileupload:commons-fileupload:1.3.2',
'commons-codec:commons-codec:1.10',
'commons-beanutils:commons-beanutils:1.9.3',
'commons-httpclient:commons-httpclient:3.1',
'org.apache.httpcomponents:fluent-hc:4.3.6',
'org.apache.httpcomponents:httpclient:4.5.3',
'org.apache.httpcomponents:httpclient-cache:4.5.3',
'org.apache.httpcomponents:httpcore:4.4.8',
'org.apache.httpcomponents:httpmime:4.5.3',
'org.apache.curator:curator-framework:4.0.1',
'org.jfree:jfreechart:1.0.19',
'org.apache.velocity:velocity:1.7',
'org.apache.poi:poi:3.16'
]
//--redis client
ref4RedisClient=["redis.clients:jedis:$globalRedisClientVersion"]
ref4Freemarker=["org.freemarker:freemarker:$globalFreemarkerVersion"]
//--这是阿里云短信引用的第三方类库
ref4AliYunSms=[
'com.aliyun:aliyun-java-sdk-core:3.2.8',
'com.aliyun:aliyun-java-sdk-dysmsapi:1.1.0'
]
//--阿里云图片裁剪
ref4AliSimpleImage=[
'com.alibaba:simpleimage:1.2.3'
]
//--阿里fast json引用地址
ref4FastJson=["com.alibaba:fastjson:$globalFastJsonVersion"]
//--json-lib引用地址
ref4JsonLib=["net.sf.json-lib:json-lib:2.4:jdk15"]
//--jdom1&jdom2以及相关api
ref4Jdom=[
'org.jdom:jdom2:2.0.6',
'org.jdom:jdom:1.1.3',
'joda-time:joda-time:2.9.7'
]
//--google guava
ref4GoogleGuava=["com.google.guava:guava:$globalGoogleGuavaVersion"]
//--dom4j
ref4Dom4j=["dom4j:dom4j:$globalDom4jVersion"]
ref4JavaMail=["javax.mail:mail:$globalJavaMailVersion"]
ref4Jsoup=["org.jsoup:jsoup:$globalJsoupVersion"]
ref4Quartz=[
"org.quartz-scheduler:quartz:$globalQuartzVersion",
"org.quartz-scheduler:quartz-jobs:$globalQuartzVersion"
]
ref4Flexmark=[
"com.vladsch.flexmark:flexmark-all:$globalFlexmarkVersion"
]
ref4PostgresqlJdbcDriver=[
"org.postgresql:postgresql:$globalPostgresqlJdbcDriverVersion"
]
ref4QiuniuSdkVersion=[
"com.qiniu:qiniu-java-sdk:$globalQiniuSdkVersion"
]
ref4ApacheAnt=["org.apache.ant:ant:$globalApacheAntVersion"]
//--二维码
ref4ZXing=[
"com.google.zxing:core:$globalGoogleZXingVersion",
"com.google.zxing:javase:$globalGoogleZXingVersion"
]
ref4FastdfsClient=["cn.bestwu:fastdfs-client-java:$globalFastdfsClientVersion"]
ref4Log4j=["log4j:log4j:$globalLog4jVersion"]
ref4Slf4jToLog4j=["org.slf4j:slf4j-log4j12:$globalSlf4jVersion"]
/****常见或者程序主要引用依赖定义 end****/
}
idea {
module {
inheritOutputDirs = true
}
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
tasks.withType(GroovyCompile) {
groovyOptions.encoding = "MacRoman"
}
repositories {
maven{
//更换为阿里的仓库
url 'http://maven.aliyun.com/nexus/content/groups/public'
}
//有些jar包在中央仓库是没有的,需要手动添加上去
// flatDir { dirs 'local_jars' }
// mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
}
xxl-conf-core的构建文件如下:
group 'com.xuxueli'
version '1.0-SNAPSHOT'
dependencies {
//--日志
compile ref4Log4j
compile ref4Slf4jToLog4j
compile "org.springframework:spring-beans:$globalSpringVersion"
}
xxl-conf-admin的构建文件如下:
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${globalSpringBootVersion}")
}
}
plugins {
id 'java'
id 'war'
id "application"
}
group 'com.xuxueli'
version '1.6.1'
dependencies {
compile (project(":xxl-conf-core"))
/***spring boot 相关依赖***/
compile "org.springframework.boot:spring-boot:$globalSpringBootVersion"
compile ("org.springframework.boot:spring-boot-starter:$globalSpringBootVersion")
compile ("org.springframework.boot:spring-boot-starter-web:$globalSpringBootVersion")
compile "org.springframework.boot:spring-boot-starter-freemarker:$globalSpringBootVersion"
compile "org.springframework.boot:spring-boot-devtools:$globalSpringBootVersion"
// compile 'ch.qos.logback:logback-classic:1.2.3'
compileOnly "org.springframework.boot:spring-boot-dependencies:$globalSpringBootVersion"
testCompile "org.springframework.boot:spring-boot-starter-test:$globalSpringBootVersion"
/****spring boot end****/
compile "org.mybatis.spring.boot:mybatis-spring-boot-starter:$globalSpringBootStaterVersionOfMyBatis"
compile "mysql:mysql-connector-java:$globalMysqlJdbcDriverVersion"
compile "org.apache.commons:commons-lang3:$globalApacheCommonLang3Version"
compile ref4PostgresqlJdbcDriver
compile "org.springframework:spring-jdbc:$globalSpringVersion"
}
//
///**
// *
// * 打包为可执行 jar
// *
// * ***/
//
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
}
}
//
mainClassName = 'com.xxl.conf.admin.XxlConfAdminApplication'
//
configurations {
providedRuntime
}
/****
*
* 注意,引入spring boot以后,默认打包的是war,
* 而且生成的执行脚本也会找到war包进行执行,就是说,
* jar的生成已经没用了,只需要用war即可。
*
* *****/
jar {
// baseName = 'conf-admin'
// version = '1.0.0'
// destinationDir='lib'
// destinationDir = file("$buildDir/lib")
manifest {
attributes 'Main-Class': 'com.xxl.conf.admin.XxlConfAdminApplication'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
///****
// * 注意,application打包的jar以及war都放在build的libs下面,但是bootscript下面的启动脚本是启动的build
// * 下面的lib下面。所以下面要添加一个文件复制操作
// * ***/
build.doLast {
copy {
from file("${rootDir}/build/libs/")
into("${rootDir}/build/lib/")
include '**/*.war'
include '*.war'
}
println "build 结束,执行文件复制操作"
}
背景完毕,根据网上的查阅资料:
解决Gradle传递性依赖冲突
注意,这里归纳一下,
下面命令:
gradle dependencyInsight --dependency slf4j-api
是用来检查具体依赖结构的。
springboot项目maven报错 LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback
上文归纳一下,就是说,日志第三方库冲突了,两个解决方案:
方案1、不要slf4j-log4j12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
方案2、不要log4j-slf4j-impl
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
其中博主的一些观点个人是很有意思的:
欸,我还是继续用成熟的spring 好了。
下面来解决问题。
问题解决
我这里解决去掉slf4j-log4j12这个依赖,首先,在xxl-conf-admin下面查找具体依赖的结构:
gradle dependencyInsight --dependency slf4j-log4j12
如下图:
注意了,结果显示,只有project xxl-conf-core引用了。好了,我们要排除这个:
compile (project(":xxl-conf-core"))
{
exclude group:"org.slf4j",module:"slf4j-log4j12"
}
然后执行:
结果如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE)
14:01:52.517 logback [restartedMain] INFO c.x.c.admin.XxlConfAdminApplication - Starting XxlConfAdminApplication on tw-server with PID 16159 (started by too-white in /home/too-white/文档/svn/clover)
14:01:52.550 logback [restartedMain] INFO c.x.c.admin.XxlConfAdminApplication - No active profile set, falling back to default profiles: default
14:01:52.583 logback [restartedMain] INFO o.s.b.d.e.DevToolsPropertyDefaultsPostProcessor - Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
14:01:52.583 logback [restartedMain] INFO o.s.b.d.e.DevToolsPropertyDefaultsPostProcessor - For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
14:01:54.362 logback [restartedMain] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 7788 (http)
14:01:54.383 logback [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-7788"]
14:01:54.391 logback [restartedMain] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
14:01:54.392 logback [restartedMain] INFO o.a.catalina.core.StandardEngine - Starting Servlet Engine: Apache Tomcat/9.0.13
14:01:54.398 logback [restartedMain] INFO o.a.c.core.AprLifecycleListener - The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib]
14:01:54.463 logback [restartedMain] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
14:01:54.463 logback [restartedMain] INFO o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 1879 ms
14:01:55.051 logback [pool-2-thread-2] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
14:01:55.167 logback [restartedMain] INFO o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
14:01:55.675 logback [pool-2-thread-2] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
14:01:55.882 logback [pool-2-thread-2] INFO c.x.c.a.s.i.XxlConfNodeServiceImpl - >>>>>>>>>>> xxl-conf, sync totel conf data success, sync conf count = 3
14:01:56.444 logback [restartedMain] INFO o.s.b.d.a.OptionalLiveReloadServer - LiveReload server is running on port 35729
14:01:56.490 logback [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-7788"]
14:01:56.495 logback [restartedMain] INFO o.a.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
14:01:56.509 logback [restartedMain] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 7788 (http) with context path ''
14:01:56.512 logback [restartedMain] INFO c.x.c.admin.XxlConfAdminApplication - Started XxlConfAdminApplication in 4.884 seconds (JVM running for 5.906)
好了,终于执行成功了。
问题再现
2019-01-31日补充。
上面我们进行了jar的依赖排错,确定了compile某个类库时候就exclude–去掉某些依赖,然而,假如是有很多库都用到了这个jar或者全部都用到,我们应该怎么解决?逐个逐个compile加上exclude?
下面就是对此的解决。
问题描述
近日要整合log4j2这个日志插件,发现冲突很多,譬如,在根项目下面添加了:
然后在allprojects 的依赖里面统一设置:
/****项目统一使用log4j2日志插件 begin***/
//--log4j2相关库
compile ref4Slf4jBindingLog4j2
compile ref4Log4j2
compile ref4Disruptor //log4j2要异步记录日志必须有这个。
//--log4j2在web项目中要有这个。
runtime "org.apache.logging.log4j:log4j-web:$globalLog4j2Version"
/****项目统一使用log4j2日志插件 end***/
然后你会发现某些项目,譬如,xxl-conf-admin运行不了,提示:
logback和log4j 2.11.1冲突了,然后按照之前的查找依赖,会发现:
gradle dependencyInsight --dependency log4j-slf4j-impl
有两个地方用到log4j,其中一个是log4j-slfj-impl,另一个是。。。compileClasspath。。
就是说,编译出来就带log4j了。。没有引入其他jar都这样了!!!
好了,说说解决方案吧:
How to exclude a jar from gradle
看到这一段了没有?我们只需要添加:
configurations {
/***xxl-conf-admin中默认使用的是logback,
* 全局引入log4j2之后冲突无法运行,没必要在这个项目引用log4j以及第二代log4j,故全部排除**/
all*.exclude group:"org.slf4j",module:"slf4j-log4j12"
all*.exclude group:"org.apache.logging.log4j",module:"log4j-slf4j-impl"
}
运行:
好了,成功运行。。其实,可以将logback排除的。。