背景

在springboot项目中,接入了两个Redis。其中一个通过spring-data-redis接入,另一个是自己封装的redis,两个必须同时存在,不可删除任何一个。springboot的版本是1.5.9
这次的例子比较特殊,如果只是需要看JNI的对应表,请直接到:参考内容

小技巧
一般排查异常问题,直接找第一个“Caused by”后面的异常信息最后一个分号后面的内容即可。

  1. 异常信息如下:
  2. Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL [jar:file:/Users/edz/install/apache-tomcat-8.5.37/webapps/ROOT/WEB-INF/lib/springfox-spring-web-2.9.2.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'endpointHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping]: Factory method 'endpointHandlerMapping' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mvcEndpoints' defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'environmentMvcEndpoint' defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]: Unsatisfied dependency expressed through method 'environmentMvcEndpoint' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration$$EnhancerBySpringCGLIB$$da7b48f9]: Constructor threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration$RedisHealthIndicatorConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConnectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration$RedisConnectionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: redis.clients.jedis.JedisPool.<init>(Lorg/apache/commons/pool2/impl/GenericObjectPoolConfig;Ljava/lang/String;IILjava/lang/String;ILjava/lang/String;Z)V
  3. 不同纠结这段有多长,直接找最后一个分号后面的内容:
  4. nested exception is java.lang.NoSuchMethodError: redis.clients.jedis.JedisPool.<init>(Lorg/apache/commons/pool2/impl/GenericObjectPoolConfig;Ljava/lang/String;IILjava/lang/String;ILjava/lang/String;Z)V

状况

项目启动到最后时,直接一句:Application startup failed,然后就是一段异常。o(≧口≦)o崩溃

解决过程

通过分析异常信息,可以得到具体的错误提示是:

  1. redis.clients.jedis.JedisPool.<init>(Lorg/apache/commons/pool2/impl/GenericObjectPoolConfig;Ljava/lang/String;IILjava/lang/String;ILjava/lang/String;Z)V

现在定位到了异常,但是如果想要理解这种异常信息,就必须有简单的JNI基础:JedisPool. 表示是一个构造器
但是仅仅知道这个还不够,因为这个类中充斥着大量的构造器,这种情况,就需要根据参数来确定具体是哪个构造器了
通过上面的构造器的参数转换出如下表:

标识 核心标识 Java类型
Lorg/apache/commons/pool2/impl/GenericObjectPoolConfig; L GenericObjectPoolConfig类
IILjava/lang/String; I、I、L 其实这是3个参数:int、int、String
Z Z Boolean

根据表格的信息发现,spring-data初始化redis时,没有使用redis官方的jar,而是使用了自己封装的redis。再一看自己封装的redis,其中竟然使用了和redis官方jar中同名的“redis.clients.jedis.JedisPool”(这中做法就不在此吐槽了。。。。)
然而这种骚操作直接导致了,spring-data在初始化redis时,懵逼了。。。。
为了保证两个redis可以同时使用,只能对spring-data-redis进行降级,保证即使使用自己封装的Redis也可以找到对应的构造器

参考内容

Java 类型 符号
Boolean Z
Byte B
Char C
Short S
Int I
Long J
Float F
Double D
Void V
objects对象 以”L”开头,以”;”结尾,中间是用”/“ 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用$来表示嵌套。例如 “(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z”

参考网站:https://wsjiang.iteye.com/blog/2116902