spring cache 详解

系列文章目录



前言

伴随信息量的爆炸式增长以及构建的应用系统越来越多样化、复杂化,特别是企业级应用互联网化的趋势,缓存(Cache)对应用程序性能的优化变的越来越重要。 将所需服务请求的数据放在缓存中,既可以提高应用程序的访问效率,又可以减少数据库服务器的压力,从而让用户获得更好的用户体验。


一、spring cache

1、简介

定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术,并支持使用 JCache(JSR-107)注解简化我们开发。SpringCache本质上不是一种缓存的实现,而是一种缓存的抽象。通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果

2、注解使用

在这里插入图片描述

1、@Cacheable

先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。注意:要缓存的实体类必须实现序列化。一般用于查找
在这里插入图片描述

2、@CachePut

和 @Cacheable 不同的是,它每次都会触发真实方法的调用 。简单来说就是用户更新缓存数据。
在这里插入图片描述

3、@CacheEvict

作用:主要针对方法配置,能够根据一定的条件对缓存进行清空 。一般用于删除和更新。
在这里插入图片描述

4、@Caching

@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解

@Caching(evict={@CacheEvict("user1"), @CacheEvict("user2",allEntries=true)})

在这里插入图片描述

5、@CacheConfig

@CacheConfig是一个类级别的注解,它允许共享缓存名称,自定义KeyGenerator,自定义CacheManager和自定义CacheResolver。

在这里插入图片描述
当我们需要缓存的地方越来越多,可以在类上使用 @CacheConfig(cacheNames = {“”}) 注解来统一指定 value 的值,这时方法可省略 value,如果你在你的方法依旧写上了 value,那么依然以方法的value值为准

@CacheConfig(cacheNames = {"myCache"})
public class UserServiceImpl implements UserService {
    @Override
    @Cacheable(key = "targetClass + methodName + #p0")//此处没写value
    public List<User> findAllLimit(int num) {
        return userRepository.findAllLimit(num);
    }
    .....
}
  • Spring支持的CacheManager,
  • SimpleCache:没有引入其他缓存组件的情况下,SpringBoot 的默认缓存,使用ConcurrentMap实现.

在这里插入图片描述

3、注意点

  • 配置文件中启用缓存:spring.cache.type=redis
  • 缓存的对象必须实现Serializable 注解是基于Spring
  • AOP代理类,内部方法调用是不走代理的,注解是不起作用的

4、spring cache 优劣

优势

  • 支持开箱即用(Out Of The Box),并提供基本的Cache抽象,方便切换各种底层Cache
  • 通过Cache注解即可实现缓存逻辑透明化,让开发者关注业务逻辑
  • 当事务回滚时,缓存也会自动回滚
  • 支持比较复杂的缓存逻辑
  • 提供缓存编程的一致性抽象,方便代码维护。

劣势

  • Spring Cache并不针对多进程的应用环境进行专门的处理。
  • 另外SpringCache抽象的操作中没有锁的概念,当多线程并发操作(更新或者删除)同一个缓存项时,有可能读取到过期的数据。
  • 查询是加锁,其他的都是公用的没有加锁。读模式做了处理,写模式并没有管
    常规(读多写少,及时性,一致性不高的数据完全可以用SpringCache)

场景问题处理

1、问题处理场景一
2、问题处理场景二
3、问题处理场景三

二、集成springboot

1、maven依赖

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

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

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>

2、 配置文件

 redis:
    host: 主机ip地址
    port: 6379
    password: '密码'
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 30000

3、启动类配置

在启动类上添加**@EnableCaching,开始缓存**

4、编写配置类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.time.Duration;

@Slf4j
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfig extends CachingConfigurerSupport {
    private static final FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);


    //缓存管理器。可以管理多个缓存
    //只有CacheManger才能扫描到cacheable注解
    //spring提供了缓存支持Cache接口,实现了很多个缓存类,其中包括RedisCache。但是我们需要对其进行配置,这里就是配置RedisCache

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
                //Redis链接工厂
                .fromConnectionFactory(connectionFactory)
                //缓存配置 通用配置  默认存储一小时
                .cacheDefaults(getCacheConfigurationWithTtl(Duration.ofHours(1)))
                //配置同步修改或删除  put/evict
                .transactionAware()
                //对于不同的cacheName我们可以设置不同的过期时间
//                .withCacheConfiguration("app:",getCacheConfigurationWithTtl(Duration.ofHours(5)))
                .withCacheConfiguration("user:",getCacheConfigurationWithTtl(Duration.ofHours(2)))
                .build();
        return cacheManager;
    }
    //缓存的基本配置对象
    private   RedisCacheConfiguration getCacheConfigurationWithTtl(Duration duration) {
        return RedisCacheConfiguration
                .defaultCacheConfig()
                //设置key value的序列化方式
                // 设置key为String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value 为自动转Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
                // 不缓存null
                .disableCachingNullValues()
                // 设置缓存的过期时间
                .entryTtl(duration);
    }

    //缓存的异常处理
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
        log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
            }
            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.error("Redis occur handleCacheClearError:", e);
            }
        };
    }

    @Override
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
   
}


5、编写代码

@Service
public class UserImpl implements UserService {

    @Resource
    private UsersMapper usersMapper;

    @Override
    public User queryNo(Integer userId) {
        System.out.println("进入无缓存方法");
        User user = usersMapper.queryUser(userId);
        return user;
    }
    
 	 /**
     * 查找缓存
     * @param userId userID
     * @return user
     */
    @Override
    @Cacheable(cacheNames ="user",key = "#userId)
    public User query(Integer userId) {
        System.out.println("进入有缓存方法");
        return usersMapper.queryUser(userId);
    }       
}

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值