Skip to content

希望支持 Jedis 3.x  #1647

Closed
Closed

Description

@prosaically

希望能够支持Jedis 3.x。

个人建议

ConfigStorage Jedis的实现构造应该统一使用WxRedisOps来构造,因为不使用统一会出现以下问题。

版本编号

  1. WxJava 3.8.0
  2. Jedis 3.2.0
  3. spring-boot-starter-data-redis 2.3

问题体现

异常环境

  1. Java JDK 1.8.0_201
  2. centos-release-7-6.1810.2.el7.centos.x86_64

环境差异

windows 10,jdk 1.8.0_171 下能正常运行。

代码实现

    @Bean
    public WxMpService wxMpService(StringRedisTemplate redis, WeChatConfig cfg) {
        RedisTemplateWxRedisOps ops = new RedisTemplateWxRedisOps(redis);
        WxMpRedisConfigImpl config = new WxMpRedisConfigImpl(ops, "wx");
        config.setAppId(cfg.getAppId());
        config.setSecret(cfg.getSecret());
        config.setToken(cfg.getToken());
        config.setAesKey(cfg.getEncodingAesKey());
        WxMpService wxService = new WxMpServiceImpl();
        wxService.setWxMpConfigStorage(config);
        return wxService;
    }

异常日志

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'wechatController': Unsatisfied dependency expressed through field 'wxMpServiceMap'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wxMpService' defined in class path resource [com/egu365/api/config/WebConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [me.chanjar.weixin.mp.api.WxMpService]: Factory method 'wxMpService' threw exception; nested exception is java.lang.NoClassDefFoundError: redis/clients/util/Pool
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:895)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	at com.egu365.api.Egu365RestApiApplication.main(Egu365RestApiApplication.java:19)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:109)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wxMpService' defined in class path resource [com/egu365/api/config/WebConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [me.chanjar.weixin.mp.api.WxMpService]: Factory method 'wxMpService' threw exception; nested exception is java.lang.NoClassDefFoundError: redis/clients/util/Pool
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1524)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1488)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1407)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1264)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	... 28 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [me.chanjar.weixin.mp.api.WxMpService]: Factory method 'wxMpService' threw exception; nested exception is java.lang.NoClassDefFoundError: redis/clients/util/Pool
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651)
	... 44 common frames omitted
Caused by: java.lang.NoClassDefFoundError: redis/clients/util/Pool
	at com.egu365.api.config.WebConfig.wxMpService(WebConfig.java:212)
	at com.egu365.api.config.WebConfig$$EnhancerBySpringCGLIB$$14eddc6.CGLIB$wxMpService$22(<generated>)
	at com.egu365.api.config.WebConfig$$EnhancerBySpringCGLIB$$14eddc6$$FastClassBySpringCGLIB$$3f0fb69c.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at com.egu365.api.config.WebConfig$$EnhancerBySpringCGLIB$$14eddc6.wxMpService(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 45 common frames omitted
Caused by: java.lang.ClassNotFoundException: redis.clients.util.Pool
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:129)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 56 common frames omitted

原因分析

使用Spring Data Redis,本生的目的是屏蔽Jedis客户端复杂和版本差异。

RedisTemplateWxRedisOps ops = new RedisTemplateWxRedisOps(template);

使用RedisTemplateWxRedisOps时,只要SpringDataRedis能正常运行,WxMpRedisConfigImpl也应该能正常运行。

  • 但由于windows 和linux下的jdk对class加载方式有差异。
 // 编号1
 // 未使用的构造
  public WxMpRedisConfigImpl(JedisPool jedisPool) {
    this(new JedisWxRedisOps(jedisPool), "wx");
  }
 // 编号2
 // 使用的构造器
  public WxMpRedisConfigImpl(WxRedisOps redisOps, String keyPrefix) {
    this.redisOps = redisOps;
    this.keyPrefix = keyPrefix;
  }

理论上来说,使用2号构造器,应该能正常使用,但是在Linux环境,Jdk确会加载1号构造器的依赖关系。
如果移除1号构造号,统一WxRedisOps来构造,就可以避免这样的问题。
WxMpRedisConfigImpl和JedisWxRedisOps(JedisPool jedisPool)没有直接依赖关系。也就不会去加载redis.clients.util.Pool。

  • 这样能够统一依赖,降压依赖的复杂程度。
  • 能够更加灵活,如果有特殊实现,也只需关注WxRedisOps接口。

_ 以上仅个人建议,希望项目越来越完善,成代码中最靓的仔 _

Activity

  1. xuxinlong001 commented on Jul 2, 2020

    @xuxinlong001

    spring-boot-starter-data-redis 已经默认支持lettuce,不支持jedis,所以一直报错,建议3.8版本去除对jedis的依赖

  2. binarywang commented on Jul 2, 2020

    @binarywang
    Owner

    感谢楼主建议,本来SDK只关注微信相关接口实现,最初是没有引入redis相关依赖的,但后期为了满足大家需求,引入了相关实现,也仅是作为参考。
    如果大家有更好的实现,欢迎随时提供PR。

  3. gyjx commented on Jul 2, 2020

    @gyjx

    嗯 这就解决了我,为什么windows下启动没错,linux启动报错的问题!谢谢

  4. binarywang commented on Jul 2, 2020

    @binarywang
    Owner

    #1605 跟此问题中提到的方法类似,准备去掉第一个构造方法

  5. added a commit that references this issue on Jul 5, 2020
  6. binarywang commented on Jul 6, 2020

    @binarywang
    Owner

    请使用最新测试版本看是否还有问题

  7. prosaically commented on Jul 6, 2020

    @prosaically
    Author

    weixin-java-miniapp 的 config WxMaRedisBetterConfigImpl 也需要调整

    public WxMaRedisBetterConfigImpl(JedisPool jedisPool) {
        this(new JedisWxRedisOps(jedisPool), "wa");
    }
    
  8. binarywang commented on Jul 6, 2020

    @binarywang
    Owner

    weixin-java-miniapp 的 config WxMaRedisBetterConfigImpl 也需要调整

    public WxMaRedisBetterConfigImpl(JedisPool jedisPool) {
        this(new JedisWxRedisOps(jedisPool), "wa");
    }
    

    建议可以自己先在本地测试,没问题后直接提交PR,这样会比较高效些

  9. added a commit that references this issue on Jul 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions