10

I have following configuration on my spring boot project.

@SpringBootApplication
@EnableTransactionManagement
@EnableCaching
@EnableScheduling
@EnableAsync
public class Application {

    String redisHost = "localhost";
    int redisPort = 6379;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(redisHost);
        factory.setPort(redisPort);
        factory.setUsePool(true);
        return factory;
    }
    @Bean
    RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        return redisTemplate;
    }
    @Bean
    public CacheManager cacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
        return cacheManager;
    }
}

Also I have following maven dependency on pom.

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

I have a separate redis server running on my local machine on defined port. Also in my service classes I have annotations like @Cacheable, @CachePut to support caching.

I can start spring boot application without errors and CRUD operations also works. But seems it is not using the defined redis cache. I used 'redi desktop manger' browsing tool and couldn't find any data on redis. Also I tried with monitoring redis server via redis cli command 'monitor', I can't see any changes on monitor.

So I assume redis caching still not working on my spring boot application. Can someone help me to figure out the issue?

I am using spring boot version 1.4.2.RELEASE

Thanks!

4 Answers 4

13

Given you are using Spring Boot, much of your Redis configuration is unnecessary since Spring Boot provides "auto-configuration" support for Redis, both as a data source as well as a caching provider.

You were also not specific about what version of Spring Boot you were using (e.g. 1.5.0.RC1) to run your application, or whether you had any application.properties on your application's classpath, which might make a difference if you explicitly specified spring.cache.type (set to something other than "redis", for instance).

However, in general, I cannot really see much wrong with your Redis or Spring Cache @Configuration class. However, it does seem to be a problem with not explicitly setting cacheManager.setUsePrefix(true). When I set this RedisCacheManager property ('usePrefix`), then everything worked as expected.

I am not (Spring Data) Redis expert so I am not exactly sure why this is needed. However, I based my test configuration on Spring Boot's "auto-configuration" support for Redis caching as well as your @Configuration "Application" class, shown above.

And, because you can eliminate most of your explicit configuration and use Spring Boot's "auto-configuration" support for Redis as a data source as well, I added a "AutoRedisConfiguration" @Configuration class to my test class. I.e. you can use this to configure Redis instead of my other @Configuration class ("CustomRedisConfiguration") which uses your configuration + the fix.

Here is the complete test example...

/*
 * Copyright 2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.spring.cache;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PostConstruct;

import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.spring.cache.CachingWithRedisIntegrationTest.CachingWithRedisConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;

/**
 * Integration tests testing Spring's Cache Abstraction using Spring Data Redis auto-configured with Spring Boot.
 *
 * To run this test, first start a Redis Server on localhost listening on the default port, 6379.
 *
 * @author John Blum
 * @see org.junit.Test
 * @since 1.0.0
 */
@RunWith(SpringRunner.class)
@ActiveProfiles("auto")
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@ContextConfiguration(classes = CachingWithRedisConfiguration.class)
@SuppressWarnings("unused")
public class CachingWithRedisIntegrationTest {

  protected static final int REDIS_PORT = 6379;
  protected static final String REDIS_HOST = "localhost";

  private AtomicBoolean setup = new AtomicBoolean(false);

  @Autowired
  private MathService mathService;

  @Autowired(required = false)
  private RedisTemplate<Object, Object> redisTemplate;

  @Before
  public void setup() {
    if (redisTemplate != null && !setup.getAndSet(true)) {
      redisTemplate.delete(Arrays.asList(0L, 1L, 2L, 4L, 8L));
    }
  }

  @Test
  public void firstCacheMisses() {
    assertThat(mathService.factorial(0L)).isEqualTo(1L);
    assertThat(mathService.wasCacheMiss()).isTrue();
    assertThat(mathService.factorial(1L)).isEqualTo(1L);
    assertThat(mathService.wasCacheMiss()).isTrue();
    assertThat(mathService.factorial(2L)).isEqualTo(2L);
    assertThat(mathService.wasCacheMiss()).isTrue();
    assertThat(mathService.factorial(4L)).isEqualTo(24L);
    assertThat(mathService.wasCacheMiss()).isTrue();
    assertThat(mathService.factorial(8L)).isEqualTo(40320L);
    assertThat(mathService.wasCacheMiss()).isTrue();
  }

  @Test
  public void thenCacheHits() {
    assertThat(mathService.factorial(0L)).isEqualTo(1L);
    assertThat(mathService.wasCacheMiss()).isFalse();
    assertThat(mathService.factorial(1L)).isEqualTo(1L);
    assertThat(mathService.wasCacheMiss()).isFalse();
    assertThat(mathService.factorial(2L)).isEqualTo(2L);
    assertThat(mathService.wasCacheMiss()).isFalse();
    assertThat(mathService.factorial(4L)).isEqualTo(24L);
    assertThat(mathService.wasCacheMiss()).isFalse();
    assertThat(mathService.factorial(8L)).isEqualTo(40320L);
    assertThat(mathService.wasCacheMiss()).isFalse();
  }

  interface MathService {
    boolean wasCacheMiss();
    long factorial(long number);
  }

  @EnableCaching
  @SpringBootConfiguration
  @Import({ AutoRedisConfiguration.class, CustomRedisConfiguration.class })
  static class CachingWithRedisConfiguration {

    @Bean
    MathService mathService() {
      return new MathService() {
        private final AtomicBoolean cacheMiss = new AtomicBoolean(false);

        @Override
        public boolean wasCacheMiss() {
          return cacheMiss.getAndSet(false);
        }

        @Override
        @Cacheable(cacheNames = "Factorials")
        public long factorial(long number) {
          cacheMiss.set(true);

          Assert.isTrue(number >= 0L, String.format("Number [%d] must be greater than equal to 0", number));

          if (number <= 2L) {
            return (number < 2L ? 1L : 2L);
          }

          long result = number;

          while (--number > 1) {
            result *= number;
          }

          return result;
        }
      };
    }

    @Bean
    @Profile("none")
    CacheManager cacheManager() {
      return new ConcurrentMapCacheManager();
    }
  }

  @Profile("auto")
  @EnableAutoConfiguration
  @SpringBootConfiguration
  static class AutoRedisConfiguration {

    @PostConstruct
    public void afterPropertiesSet() {
      System.out.println("AUTO");
    }

    @Bean
    static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
      PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer =
        new PropertySourcesPlaceholderConfigurer();
      propertySourcesPlaceholderConfigurer.setProperties(redisProperties());
      return propertySourcesPlaceholderConfigurer;
    }

    static Properties redisProperties() {
      Properties redisProperties = new Properties();

      redisProperties.setProperty("spring.cache.type", "redis");
      redisProperties.setProperty("spring.redis.host", REDIS_HOST);
      redisProperties.setProperty("spring.redis.port", String.valueOf(REDIS_PORT));

      return redisProperties;
    }
  }

  @Profile("custom")
  @SpringBootConfiguration
  static class CustomRedisConfiguration {

    @PostConstruct
    public void afterPropertiesSet() {
      System.out.println("CUSTOM");
    }

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
      JedisConnectionFactory factory = new JedisConnectionFactory();
      factory.setHostName(REDIS_HOST);
      factory.setPort(REDIS_PORT);
      factory.setUsePool(true);
      return factory;
    }

    @Bean
    RedisTemplate<Object, Object> redisTemplate() {
      RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
      redisTemplate.setConnectionFactory(jedisConnectionFactory());
      return redisTemplate;
    }

    @Bean
    CacheManager cacheManager() {
      RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
      cacheManager.setUsePrefix(true); // THIS IS NEEDED!
      return cacheManager;
    }
  }
}

Hope this helps!

Cheers, John

Sign up to request clarification or add additional context in comments.

2 Comments

Note: for my test class I was running with Spring Boot 1.5.0.RC1 as well as the spring-boot-starter-data-redis. In my AutoRedisConfiguration class, I explicitly set the spring.cache.type to Redis since I have other examples in my test project that use other caching providers, like Pivotal GemFire, Apache Geode and Hazelcast, but this is not strictly necessary if you don't have other possible caching providers in Spring Boot's auto-configuration support on your classpath.
Thanks I will let you know the results. My spring boot version is 1.4.2.RELEASE
2

I am using Spring Boot 2.0 and its very simple to use Redis for simple caching purpose.

  1. Annotate your Spring boot application with @EnableCaching
  2. In your application.properties have these properties

    spring.cache.type=redis redis.host.url= redis.host.port=

  3. Annotate your methods with @Cacheable.

That's it!!

If you are using AWS Elasticache and you have checked the in-transit encryption then you need to add a RedisConfiguration file to set your ssl to true.

Spring Boot 2.0 now uses LettuceConnectionFactory.

To do the above simply add a class and mark it with @Configuration annotation and add the following bean

@Bean
  public LettuceConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
    configuration.setHostName(redisHost);
    configuration.setPort(redisPort);
    return new LettuceConnectionFactory(configuration,            LettuceClientConfiguration.builder().useSsl().disablePeerVerification().build());
  }

4 Comments

It says Caused by: java.lang.IllegalArgumentException: No cache manager could be auto-configured, check your configuration (caching type is 'REDIS')
@Sukalpo when I tried this I got this error, *************************** APPLICATION FAILED TO START *************************** Description: A component required a bean named 'cacheManager' that could not be found.
@PRAJINPRAKASH have you annotated your spring boot with @EnableCachinng?
@BishwasMishra - have you added spring.cache.type=redis in your applicationm.properties?
1

You can check existence of your keys in redis with command in redis-cli: keys *

If there will be your key, everything is OK :)

Comments

0

The methods of your classes should be @Cacheable. Along with @CachePut you should use @CacheEvict with the keys and their values for delete method to flush out the resources. Also the host, port, type and password is needed in application.properties file.

`spring.redis.password= password`

`spring.cache.type=redis`
`spring.redis.host=localhost`
`spring.redis.port=6379`

Also add some more properties like so, as per requirement.

`spring.cache.redis.time-to-live=600000`
`spring.cache.redis.cache-null-values=false`
`spring.cache.redis.use-key-prefix=true

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.