系列文章目录
文章目录
前言
伴随信息量的爆炸式增长以及构建的应用系统越来越多样化、复杂化,特别是企业级应用互联网化的趋势,缓存(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"})publicclassUserServiceImplimplementsUserService{@Override@Cacheable(key ="targetClass + methodName + #p0")//此处没写valuepublicList<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、编写配置类
importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.parser.ParserConfig;importcom.alibaba.fastjson.serializer.SerializerFeature;importcom.fasterxml.jackson.databind.ObjectMapper;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;importorg.springframework.boot.autoconfigure.data.redis.RedisProperties;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.springframework.cache.Cache;importorg.springframework.cache.CacheManager;importorg.springframework.cache.annotation.CachingConfigurerSupport;importorg.springframework.cache.annotation.EnableCaching;importorg.springframework.cache.interceptor.CacheErrorHandler;importorg.springframework.cache.interceptor.KeyGenerator;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.cache.RedisCacheConfiguration;importorg.springframework.data.redis.cache.RedisCacheManager;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.serializer.*;importjava.lang.reflect.Method;importjava.nio.charset.Charset;importjava.time.Duration;@Slf4j@Configuration@EnableCaching@EnableConfigurationProperties(RedisProperties.class)publicclassCacheConfigextendsCachingConfigurerSupport{privatestaticfinalFastJsonRedisSerializer<Object> fastJsonRedisSerializer =newFastJsonRedisSerializer<>(Object.class);//缓存管理器。可以管理多个缓存//只有CacheManger才能扫描到cacheable注解//spring提供了缓存支持Cache接口,实现了很多个缓存类,其中包括RedisCache。但是我们需要对其进行配置,这里就是配置RedisCache@BeanpublicCacheManagercacheManager(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;}//缓存的基本配置对象privateRedisCacheConfigurationgetCacheConfigurationWithTtl(Duration duration){returnRedisCacheConfiguration.defaultCacheConfig()//设置key value的序列化方式// 设置key为String.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(newStringRedisSerializer()))// 设置value 为自动转Json的Object.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))// 不缓存null.disableCachingNullValues()// 设置缓存的过期时间.entryTtl(duration);}//缓存的异常处理@Bean@OverridepublicCacheErrorHandlererrorHandler(){// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
log.info("初始化 -> [{}]","Redis CacheErrorHandler");returnnewCacheErrorHandler(){@OverridepublicvoidhandleCacheGetError(RuntimeException e,Cache cache,Object key){
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);}@OverridepublicvoidhandleCachePutError(RuntimeException e,Cache cache,Object key,Object value){
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);}@OverridepublicvoidhandleCacheEvictError(RuntimeException e,Cache cache,Object key){
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);}@OverridepublicvoidhandleCacheClearError(RuntimeException e,Cache cache){
log.error("Redis occur handleCacheClearError:", e);}};}@Override@Bean("myKeyGenerator")publicKeyGeneratorkeyGenerator(){returnnewKeyGenerator(){@OverridepublicObjectgenerate(Object target,Method method,Object... params){StringBuffer sb =newStringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());for(Object obj : params){
sb.append(obj.toString());}return sb.toString();}};}}
5、编写代码
@ServicepublicclassUserImplimplementsUserService{@ResourceprivateUsersMapper usersMapper;@OverridepublicUserqueryNo(Integer userId){System.out.println("进入无缓存方法");User user = usersMapper.queryUser(userId);return user;}/**
* 查找缓存
* @param userId userID
* @return user
*/@Override@Cacheable(cacheNames ="user",key = "#userId)publicUserquery(Integer userId){System.out.println("进入有缓存方法");return usersMapper.queryUser(userId);}}
版权归原作者 Gary董 所有, 如有侵权,请联系我们删除。