spring-boot - 在 spring boot 中启用 Redis 缓存

标签 spring-boot redis spring-data-redis spring-cache

我的 spring boot 项目有以下配置。

@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;
    }
}

我也有以下对 pom 的 maven 依赖。

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

我有一个单独的 redis 服务器在我的本地计算机上定义的端口上运行。同样在我的服务类中,我有像@Cacheable、@CachePut 这样的注释来支持缓存。

我可以毫无错误地启动 spring boot 应用程序,并且 CRUD 操作也可以正常工作。但似乎它没有使用定义的 redis 缓存。我使用'redi desktop manger'浏览工具,在redis上找不到任何数据。我还尝试通过 redis cli 命令“monitor”监视 redis 服务器,但在监视器上看不到任何更改。

所以我假设 redis 缓存在我的 spring boot 应用程序上仍然不起作用。有人可以帮我弄清楚这个问题吗?

我正在使用 spring boot 版本 1.4.2.RELEASE

谢谢!

最佳答案

鉴于您正在使用 Spring Boot,您的大部分 Redis 配置都是不必要的,因为 Spring Boot 为 Redis 提供“自动配置”支持, 都是 data source以及 caching provider .

您也没有具体说明您使用的 Spring Boot 版本(例如 1.5.0.RC1 )来运行您的应用程序,或者您是否有任何 application.properties在您的应用程序的类路径上,如果您明确指定 spring.cache.type,这可能会有所不同(例如,设置为“redis”以外的其他内容)。

但是,总的来说,我真的看不出您的 Redis 或 Spring Cache 有什么问题 @Configuration类(class)。但是,未明确设置 cacheManager.setUsePrefix(true) 似乎确实存在问题.当我设置这个 RedisCacheManager属性 ('usePrefix`),然后一切都按预期工作。

我不是 (Spring Data) Redis 专家,所以我不确定为什么需要这样做。但是,我的测试配置基于 Spring Boot 的 "auto-configuration" support for Redis caching以及你的@Configuration “应用程序”类,如上所示。

而且,因为您可以消除大部分显式配置并使用Spring Boot "auto-configuration" support for Redis同样作为数据源,我添加了一个 "AutoRedisConfiguration" @Configuration类到我的测试类。 IE。您可以使用它来配置 Redis 而不是我的其他 @Configuration类 ( "CustomRedisConfiguration" ),它使用您的配置 + 修复

这是完整的测试示例...

/*
 * 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;
    }
  }
}

希望这对您有所帮助!

干杯, 约翰

关于spring-boot - 在 spring boot 中启用 Redis 缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41647382/

相关文章:

javascript - 无法从 Azure Web App 连接到 Azure Redis 缓存

spring - Redis - 为什么使用 Spring Data Redis 将细节保存为 HASH 和 SET?

logging - Spring Boot - 如何为每个配置文件拥有不同的日志配置?

java - 是否可以使用Java和kotlin创建Spring Boot应用程序?

Azure Redis 缓存 : "role discovery data is unavailable"

python - 如何在 Flask-Restful 中使用 Flask-Cache

redis - 将 Spring 缓存与 Redis 结合使用时,在特定时间设置过期键

spring - 如果只做简单缓存,Redis 缓存比 Spring 缓存有优势吗?

java - 如何在 IDE 中将应用程序作为 jar 运行,但将 maven 打包为 war?

spring-boot - Spring的@ComponentScan exceptFilters多个过滤器