• Spring Cache


    为什么要使用 Spring Cache 管理缓存?

    让 Spring 来管理 Bean 的缓存具有以下优势:

    1. Spring 支持 HashMap 缓存,Redis 缓存以及自定义的缓存方式;
    2. Spring 缓存几乎不需要写代码,只需要配置好并声明好注解。

    快速开始

    (1)依赖引入

    这里使用 Spring 的依赖管理器来管理 Spring Cache 的版本,会自动处理内部的模块间依赖,这也是推荐的方式。

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
        }
    }
    
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        compile("org.springframework.boot:spring-boot-starter-cache")
    }
    

    (2)启用缓存

    在 SpringApplication 配置类的地方添加以下注解以启用缓存功能。

    @SpringBootApplication
    @EnableCaching
    

    (3)ConcurrentHashMap 缓存

    当没有配置其他缓存库时,默认使用 ConcurrentHashMap 作为缓存仓库。

    (3.1)一个简单的实体类

    public class Customer {
        public String id;
        public String firstName;
        public String lastName;
        
        public Customer() {}
    
        public Customer(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
    

    (3.2)一个 Repository

    public class CustomerRepository {
        
        @Cachable
        Customer getByFirstName(String firstName){
            // 这里应该是从数据库查询数据,DEMO 简省成直接新建了。
            return new Customer(firstName, "Jobs");
        }
    }
    

    (3.3)测试一下

    如果缓存成功了,那么以下代码执行结果的 HashCode 是一致的。

    @Component
    public class AppRunner implements CommandLineRunner {
    
        private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);
    
        private final CustomerRepository customerRepository;
    
        public AppRunner(CustomerRepository customerRepository) {
            this.customerRepository = customerRepository;
        }
    
        @Override
        public void run(String... args) throws Exception {
            logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
            logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
            logger.info("John -->" + customerRepository.getByFirstName("John").hashCode());
        }
    }
    

    (4)配合 Redis 缓存

    (4.1)添加 Redis 依赖

    在前面的依赖之下再额外新增 Redis 相关的依赖,如下:

    // 本环境中的 spring-data-redis 为 1.8.7.RELEASE 版本
    // 高版本的配置略有不同,请留意
    compile ("org.springframework.data:spring-data-redis") 
    compile "redis.clients:jedis:2.9.0"
    

    (4.2)配置 Redis

    @Configuration
    public class RedisConfig {
      private final static Logger LOGGER = LoggerFactory.getLogger(RedisConfig.class);
      private final static Map<String, Long> CACHE_EXPIRE_MAP = new HashMap<>();
    
      static {
        CACHE_EXPIRE_MAP.put("cache1", 5 * 60L); //second
      }
        
      @Bean
      RedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
        jedisConFactory.setHostName("localhost");
        jedisConFactory.setPort(6379);
        return jedisConFactory;
      }
    
      @Bean
      StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
      }
    
      @Bean
      RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        template.afterPropertiesSet();
        // 默认为 JdkSerializationRedisSerializer, 配合 @Cacheable 时 KEY 会有序列化值在中间
        // 使用 StringRedisSerializer 则不会如此
        template.setKeySerializer(new StringRedisSerializer());
        return template;
      }
    
      @Bean
      public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cm = new RedisCacheManager(redisTemplate);
        cm.setCacheNames(CACHE_EXPIRE_MAP.keySet());
        cm.setExpires(CACHE_EXPIRE_MAP);
        cm.setUsePrefix(true);
        cm.setCachePrefix(cacheName -> (cacheName + ":").getBytes());
        return cm;
      }
    }
    

    (4.2)序列化实体类

    Spring 在将实体类缓存到 Redis 中时进行了序列化操作,如果不对实体类进行序列化将会报错。

    public class Customer implements Serializable {
        public String id;
        public String firstName;
        public String lastName;
        
        public Customer() {}
    
        public Customer(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
    

    (4.3)在需要缓存的位置使用注解,并指定缓存名

    如果在使用 Redis 缓存时,没有指定缓存名,将会报错:no cache could be resolved for at least one cache should be provided per cache operation

    public class CustomerRepository {
        
        @Cachable("cache1")
        Customer getByFirstName(String firstName){
            // 这里应该是从数据库查询数据,DEMO 简省成直接新建了。
            return new Customer(firstName, "Jobs");
        }
    }
    

    (4.4)测试一下

    测试代码同(3.3)。除此之外还可以通过 Redis CLI 检验缓存结果:

    > KEYS *
    1) "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
    > GET "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
    > TTL "cache1:cb5775e6-1b39-4f63-85c8-13f134a54f32"
    

    更进一步

    创建自定义的 KeyGenerator

    1. 使上述的 RedisConfig 继承 CachingConfigurerSupport,这一步很重要,否则创建自定义的 KeyGenerator 失败;
    2. 使用 @Bean 声明自定义的 KeyGenerator。代码如下:
    @Configuration
    public class RedisConfig extends CachingConfigurerSupport {
      @Bean
      @Override
      public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator() {
          @Override
          public Object generate(Object target, Method method, Object... params) {
              // 这里使用 [`] 分割参数,更进一步的还可以加入 method 名,或者直接重写一个 KeyGenerator。
              return super.generate(target, method, StringUtils.arrayToDelimitedString(params, "`"));
          }
        };
      }
    }
    

    这样,就可以覆盖 Spring Cache 默认的 SimpleKeyGenerator 了。

    参考

    1. Caching Data with Spring - spring.io
    2. Caching - spring.io
    3. A Guide To Caching in Spring - baeldung.com
    4. Spring Data Redis
    5. spring使用redis做缓存 - cnblogs.com
    6. Spring Cache – Creating a Custom KeyGenerator
    写在后面:

    1. 子曰:「学而不思则罔,思而不学则殆」。
    2. 站点地图
    2. 本作品作者为 Lshare,采用知识共享署名 4.0 国际许可协议进行许可。
  • 相关阅读:
    降龙十八掌之三:(见龙在田)优化查询性能
    完整的项目工程目录结构
    降龙十八掌之一:(亢龙有悔)SQL Server Profiler和数据库引擎优化顾问
    ASP.NET状态管理的总结
    LINQ 图解
    获取IP城市
    Eclipse 编译StanfordNLP
    Centos JAVA Eclipse
    关掉PUTTY后,进程仍可以运行。
    centos lnmp 安装笔记
  • 原文地址:https://www.cnblogs.com/lshare/p/11334432.html
Copyright © 2020-2023  润新知