springboot-how to solve JedisShardInfo or JedisPoolConfig not found exception with spring boot application ?

1. Problem

When we run a springboot application that access a redis server, sometimes, we would get exception or error as follows:

/Users/bswen/private/bw/bswen-github/bswen-springboot23/app10/src/main/java/com/bswen/app10/config/RedisConfiguration.java:32: Error: can not find JedisPoolConfig
        return new JedisConnectionFactory(config);
               ^
  class redis.clients.jedis.JedisPoolConfig not found

Or this error:

/Users/bswen/private/bw/bswen-github/bswen-springboot23/app10/src/main/java/com/bswen/app10/config/RedisConfiguration.java:54: Error: can not find JedisShardInfo
        return new JedisConnectionFactory(config);
               ^
  class redis.clients.jedis.JedisShardInfo not found

Or this error:

java.lang.ClassNotFoundException: redis.clients.jedis.util.Pool
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_121]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_121]

The application is correctly configured, I promise!!!

2. Environment

  • Springboot 2.x

3. Code

3.1 The project dependencies

We use maven as the dependency management tool, this is the pom.xml:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <start-class>Application</start-class>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <artifactId>spring-boot-multiredis2</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

You can see that we depend on these:

  • spring boot 2.4.2
  • spring data redis

3.2 The redis configuration class

We define a class for redis configuration as follows:

public class RedisCommonProperty {
    private String host;
    private int port;
    private int database;
    //getters and setters ignored
}

@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class Redis1Property extends RedisCommonProperty {
}

The above two classes are used to load property files into java. We still need to tell spring boot how to build redis connection and redisTemplate:

@Configuration
public class Redis1Configuration {

    @Autowired
    private Redis1Property redis1Property;

    @Primary
    @Bean(name = "redis1ConnectionFactory")
    public RedisConnectionFactory redis1ConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(
                redis1Property.getHost(), redis1Property.getPort());
        return new JedisConnectionFactory(config);
    }

    @Bean(name = "redis1StringRedisTemplate")
    public StringRedisTemplate userStringRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        return stringRedisTemplate;
    }

    @Bean(name = "redis1RedisTemplate")
    public RedisTemplate userRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        //setSerializer(stringRedisTemplate);
        return stringRedisTemplate;
    }

}

The above code’s key points are as follows:

  • The redis1ConnectionFactory method returns a RedisConnectionFactory, which is required by the redisTemplate, it’s the connection factory for redis connections.
  • The userStringRedisTemplate and userRedisTemplate are both RedisTemplate class to be used by applications.

3.3 Test the redis connection

Now we can test the connection, the code is as follows:

@Component
public class MultiRedisTestRunner implements CommandLineRunner {
    private final static Logger logger = LoggerFactory.getLogger(MultiRedisTestRunner.class);

    @Autowired
    @Qualifier("redis1StringRedisTemplate")
    private StringRedisTemplate stringRedisTemplate;

    //@Override
    public void run(String... strings) throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        try {
            for (int i = 0; i < 1; i++) {
                logger.info("=====================================================================");
                logger.info("start loop " + i);
                String key = "key" + i;
                stringRedisTemplate.opsForValue().set(key, "value" + i);

                String primaryKeyValue = stringRedisTemplate.opsForValue().get(key);

                logger.info("=====================================================================");
                logger.info(String.format("read from the redis1, key %s value is %s", key, primaryKeyValue));
            }
        }finally {
            latch.await();
        }
    }
}

When we run the code, the exception occurred:

  class redis.clients.jedis.JedisPoolConfig not found
  class redis.clients.jedis.JedisShardInfo not found

How to solve this problem?

4. Reason

JedisPoolConfig is needed when we use Jedis Connector to connect from springboot to redis server. In Spring Boot 2.0, spring-boot-starter-data-redis uses Lettuce connector by default instead of Jedis connector. To use Jedis connector, we need to exclude Lettuce from spring-boot-starter-data-redis.

5. Solution

We should change our pom.xml as follows:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

Rerun our tests, We got this exception:

rg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'multiRedisTestRunner': Unsatisfied dependency expressed through field 'stringRedisTemplate'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redis1StringRedisTemplate' defined in class path resource [com/bswen/sbr2/config/Redis1Configuration.class]: Unsatisfied dependency expressed through method 'userStringRedisTemplate' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redis1ConnectionFactory' defined in class path resource [com/bswen/sbr2/config/Redis1Configuration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.redis.connection.RedisConnectionFactory]: Factory method 'userRedisConnectionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: redis/clients/jedis/util/Pool
...
Caused by: java.lang.ClassNotFoundException: redis.clients.jedis.util.Pool
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_121]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_121]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_121]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_121]
	... 58 common frames omitted

After some googling, I found that we should upgrade our jedis dependency to match the spring boot version:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

Rerun our test, we got this:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.2)

2021-03-06 21:40:13.024  INFO 22247 --- [           main] com.bswen.sbr2.MultiRedisApplication2    : Starting MultiRedisApplication2 using Java 1.8.0_121 on MBP-bswen with PID 22247 (/Users/bswen/private/bw/bswen-github/bswen-project/spring-boot-multiredis2/target/classes started by bswen in /Users/bswen/private/bw/bswen-github/bswen-project)
2021-03-06 21:40:13.029  INFO 22247 --- [           main] com.bswen.sbr2.MultiRedisApplication2    : No active profile set, falling back to default profiles: default
2021-03-06 21:40:13.714  INFO 22247 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-03-06 21:40:13.717  INFO 22247 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-03-06 21:40:13.746  INFO 22247 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9 ms. Found 0 Redis repository interfaces.
2021-03-06 21:40:14.310  INFO 22247 --- [           main] com.bswen.sbr2.MultiRedisApplication2    : Started MultiRedisApplication2 in 1.719 seconds (JVM running for 2.252)
2021-03-06 21:40:14.312  INFO 22247 --- [           main] c.b.sbr2.service.MultiRedisTestRunner    : =====================================================================
2021-03-06 21:40:14.312  INFO 22247 --- [           main] c.b.sbr2.service.MultiRedisTestRunner    : start loop 0
2021-03-06 21:40:14.381  INFO 22247 --- [           main] c.b.sbr2.service.MultiRedisTestRunner    : =====================================================================
2021-03-06 21:40:14.381  INFO 22247 --- [           main] c.b.sbr2.service.MultiRedisTestRunner    : read from the redis1, key key0 value is value0

It works!

6. Summary

Now, we know that the ‘JedisPoolConfig not found’ or ‘JedisShardInfo not found’ exceptions are caused by the conflict of the default lettuce and jedis connector of spring boot applications. If we insist on Jedis, we should exclude Lettuce. And for compatible reason, we should upgrade to jedis client library to 3.30+ to match spring boot version. Thanks for your reading.

All the code are uploaded to github.com, please check this link.

-->