SpringBoot缓存使用

一.概念

1.主要注解:

注解总览:

  • @EnableCaching: 开启spring cache功能
  • @Cacheable:添加缓存
  • @CacheEvict:删除缓存
  • @CachePut:更新缓存,方法依旧执行,通常用于更新方法
  • @Caching:在一个方法中同时使用多个缓存规则
  • @CacheConfig:在类级别上设置一些通用的属性

2.@Cacheable/@CachePut/@CacheEvict 主要的参数:

value: 缓存的key值,必须指定至少一个,可指定多个 ,指定多个将依次查询缓存直到命中 。
例如:@Cacheable(value=”cache”) 或者@Cacheable(value={”cache1”,”cache2”})
key: 缓存的key值,可为空,若为空按照KeyGenerator的生成规则来。支持 SpEL 表达式。
例如:@Cacheable(value=”cache”,key=”#id”)
condition:指定缓存条件,可以为空,支持 SpEL ,返回 true 或者 false,只有为 true 才进行缓存/清除缓存。
例如:@Cacheable(value=”cache”,condition=”#name.length()>2”)
unless:否定缓存。当条件结果为TRUE时,就不会缓存。
例如:@Cacheable(value=”cache”,unless=”#name.length()>2”)
allEntries:(@CacheEvict ) 是否清空value定义的所有缓存,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
例如:@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation:(@CacheEvict) 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存
例如:@CachEvict(value=”cache”,beforeInvocation=true)

3.SpEL上下文数据:

注意:

  • 当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如@Cacheable(key = "targetClass + methodName +#p0")
  • 使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:@Cacheable(value="cache", key="#id") @Cacheable(value="cache", key="#p0")

4.SpEL运算符:

二.使用

背景简述: 
       自Spring3.1开始,Spring就自带了对缓存的支持。我们可以直接使用Spring缓存技术将某些数据放入本机的缓存中;Spring缓存技术也可以搭配其他缓存中间件(如Redis等)进行使用,将某些数据写入到缓存中间件(缓存中间件可能在其他机器上)中。

1.引入缓存

  • spring-boot中引入缓存

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

2.使用@EnableCaching启动缓存

  • 启用缓存

@SpringBootApplication
@EnableCaching
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
# 使用Redis做缓存
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        return RedisCacheManager.builder(factory).build();
    }

}

3.使用

@EnableCaching:开关性注解,在项目启动类或某个配置类上使用此注解后,则表示允许使用注解的方式进行缓存操作,如:

@Cacheable:可用于类或方法上;在目标方法执行前,会根据key先去缓存中查询看是否有数据,有就直接返回缓存中的key对应的value值。不再执行目标方法;无则执行目标方法,并将方法的返回值作为value,并以键值对的形式存入缓存,如:

@CachePut:可用于类或方法上;在执行完目标方法后,并将方法的返回值作为value,并以键值对的形式存入缓存中,如:

@CacheEvict:可用于类或方法上;在执行完目标方法后,清除缓存中对应key的数据(如果缓存中有对应key的数据缓存的话),如:

@Caching:此注解即可作为@Cacheable、@CacheEvict、@CachePut三种注解中的的任何一种或几种来使用,如:

@CacheConfig:@Cacheable、@CacheEvict、@CachePut这三个注解的cacheNames属性是必填项(或value属性是必填项,因为value属性是cacheNames的别名属性);如果上述三种注解都用的是同一个cacheNames的话,那么在每此都写cacheNames的话,就会显得麻烦。如将@CacheConfig注解就是来配置一些公共属性(如:cacheNames、keyGenerator等)的值的,如:

4.缓存注解的常用属性(以示例进行说明):

key:

        key的来源可分为三类,分别是:默认的、keyGenerator生成的、主动指定的。下面在具体代码中进行说明,注意阅读注释说明!

默认key:

keyGenerator生成key:

编写配置类、定制化key生成器:

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.lang.reflect.Method;

@Configuration
public class MyCachingConfigurer extends CachingConfigurerSupport {
 
    /**
     * 定制化key生成器
     *
     * 设置  全限定类名 + 方法名 + 参数名 共同组成 key
     *
     * @return key生成器
     * @date 2019/4/12 14:09
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (Object target, Method method, Object... params) -> {
            StringBuilder sb = new StringBuilder(16);
            sb.append(target.getClass().getName());
            sb.append("_");
            sb.append(method.getName());
            sb.append("_");
            for (int i = 0; i < params.length; i++) {
                sb.append(params[i]);
                if (i < params.length - 1) {
                    sb.append(",");
                }
            }
            return sb.toString();
        };
    }
}

此时,若使用缓存注解时不指定key属性,那么就会默认采用Key生成器生成的注解:

主动指定key:

condition:

       在激活注解功能前,进行condition验证,如果condition结果为true,则表明验证通过,缓存注解生效;否则缓存注解不生效。

condition作用时机在:缓存注解检查缓存中是否有对应的key-value 之前。注:缓存注解检查缓存中是否有对应的key-value 在 运行目标方法之前,所以 condition作用时机也在运行目标方法之前。

实验示例:

cacheNames:
         通过cacheNames对数据进行隔离,不同cacheName下可以有相同的key。也可称呼cacheName为命名空间。

下面验证的是:当同时制定多个cacheName时,从哪一个cacheName取数据。

这里先给出结论:

若属性cacheNames(或属性value)指定了多个命名空间,当进行缓存存储时:

  • 会在这些命名空间下都存一份key-value;
  • 当进行缓存读取时,会按照cacheNames值里命名空间的顺序,挨个挨个从命名空间中查找对应的key,如果在某个命名空间中查找打了对应的缓存,就不会再查找排在后面的命名空间,也不会再执行对应方法,直接返回缓存中的value值。

实验示例:

unless:
       功能是:是否令注解(在方法执行后的功能)不生效;若unless的结果为true,则(方法执行后的功能)不生效;若unless的结果为false,则(方法执行后的)功能生效。

注:unless默认为"",即相当于默认为false。

unless的作用时机:目标方法运行后。
                              注:如果(因为直接从缓存中获取到了数据,而导致)目标方法没有被执行,那么unless字段不生效。

举例说明一:
        对于@Cacheable注解,在执行目标方法前,如果从缓存中查询到了数据,那么直接返回缓存中的数据;如果从 缓存中没有查询到数据,那么执行目标方法,目标方法执行完毕之后,判断unless的结果,若unless的结果为true,那么不缓存方法的返回值;若unless的结果为false,那么缓存方法的返回值。

举例说明二:
        对于@CachePut注解,在目标方法执行完毕之后,判断unless的结果,若unless的结果为true,那么不缓存方法的返回值;若unless的结果为false,那么缓存方法的返回值。

注:因为unless的作用时机是在方法运行完毕后,所以我们可以用SpEL表达式#result 来获取方法的返回值。

实验示例:

allEntries:
        此属性主要出现在@CacheEvict注解中,表示是否清除指定命名空间中的所有数据,默认为false。

beforeInvocation:
        此属性主要出现在@CacheEvict注解中,表示 是否在目标方法执行前使 此注解生效。 默认为false,即:目标方法执行完毕后此注解生效。

 

SpringBoot 2.x 整合 redis 做缓存并对每个缓存空间设置过期时间

注意:

最后定位到,原因在使用redis存取数据时,参数有Object类,需要关系其序列化的问题。同时,因为使用redisTemplate其本身已经进行了序列化,那在没有特殊要求的情况下,不要额外再做序列化操作,会导致重复序列化产生的类型转换异常

 @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        //配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
//                .entryTtl(Duration.ZERO)
                .entryTtl(Duration.ofSeconds(15L))      //设置默认缓存15秒
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("emp");
        cacheNames.add("dept");

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("emp", config);
        configMap.put("dept", config.entryTtl(Duration.ofSeconds(30L)));  //这个缓存空间30秒

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .initialCacheNames(cacheNames)// 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }

 


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值