Spring Boot与缓存

今天学习到了Spring Boot的缓存部分

1.spring的缓存抽象简介

Spring 从3.1版本开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口来同一不同的缓存技术,以达到简化开发的目的。
并支持使用JCache(JSR107)注解简化我们的开发。

Cache接口为缓存的组件规范定义,包含各种操作集合;
Cache接口下Spring提出了各种xxxCache的实现,比如RedisCache,EhCacheCache,ConcurrentMapCache等。

每次调用需要缓存功能的方法时,spring会检查指定参数的指定目标方法的结果是否已经被缓存过,如果有就直接从缓存中获取,如果没有就先调用方法,从数据库中查询结果返回给用户,在进行缓存。
所以我们使用Spring缓存抽象的时候只用关注一下两点

1:确定方法需要被缓存,以及他们的缓存策略(要用什么当做缓存的key,什么样的条件下才进行缓存) 2:从缓存中读取之前缓存存储的数据

首先我们尝试一下基于注解的缓存开发

步骤1:开启基于注解的缓存,在主方法上加上@EnableCaching注解
2:标注缓存注解即可。
主方法

@SpringBootApplication
@MapperScan("com.dsk.dskoa1.mapper")
@EnableCaching
public class Dskoa1Application {

    public static void main(String[] args) {
        SpringApplication.run(Dskoa1Application.class, args);
    }

}
    @Cacheable(cacheNames = {"userInfo"},key = "#token",condition = "#token != null",unless = "#result == null")
    public User getUserInfo(String token,String username){

        User user = userMapper.getUserByUserName(username);
        if (user == null)
            return null;
        if (token!= null)
            user.setPassword("");
        return user;

    }

其中@Cacheable就是在方法执行前去相应的缓存中查找数据,有就直接返回,没有就执行方法,然后将结果缓存并返回

这个注解有几个属性 :

cacheNames/value:指定缓存组件的名字
key:缓存数据使用的key;可以用他来指定。默认是方法参数的值
或者用SpEL表达式;#id,参数id的值, #result返回结果的对象
keyGenerator:key的生成器,可以按照某种策略生成key,可以自己定义
CacheManager 指定缓存管理器;或者cacheResolver指定获取解析器。
condition:符合条件的情况下才缓存。
unless:符合条件的情况下不缓存。
所以上述java代码的结果是将缓存存入名为userInfo的缓存组件中,key的值是传入的参数token的值,只有当token不为空的时候才缓存,如果返回结果是空的时候也不缓存。

@Cacheable的运行流程

@Cacheable
Spring的CacheAutoConfiguration对缓存的自动配置中
导入了所有的缓存配置

    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }

        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];

            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }

            return imports;
        }
    }

这段代码就是导入各种xxxCacheConfiguration
其中包括
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
等10个不同缓存技术的自动配置类。其中默认使用的是SimpleCacheConfiguration

运行流程

1:方法运行之前,先去查找Cache(缓存组件)按照cacheNames指定的名字来获取
所有的缓存自动配置类都向容器中添加了类似xxxCacheManager的组件,他们都实现了CacheManager接口,其中的getCache方法,可以通过名字来获取一个Cache组件,用来对缓存进行操作。

public interface CacheManager {

	/**
	 * Return the cache associated with the given name.
	 * @param name the cache identifier (must not be {@code null})
	 * @return the associated cache, or {@code null} if none found
	 */
	@Nullable
	Cache getCache(String name);

	/**
	 * Return a collection of the cache names known by this manager.
	 * @return the names of all caches known by the cache manager
	 */
	Collection<String> getCacheNames();

}

而在基本上所有的CacheManager实现类中,它还会判断你想要获取的CacheName是否存在,如果不存在则会自动创建一个相应名字的Cache。
例如ConcurrentMapCacheManager

	public Cache getCache(String name) {
		Cache cache = this.cacheMap.get(name);
		if (cache == null && this.dynamic) {
			synchronized (this.cacheMap) {
				cache = this.cacheMap.get(name);
				if (cache == null) {
					cache = createConcurrentMapCache(name);
					this.cacheMap.put(name, cache);
				}
			}
		}
		return cache;
	}

2:去Cache中查找缓存内容,使用一个key,默认就是方法的参数
key是按照某种策略生成的,默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成Key
3:没有查到缓存就调用目标方法
4:将返回结果放进缓存中。

除了Cacheable之外,还有几个注解。 @CachePut

与@Cacheable的区别是,方法运行后,在存入缓存。可以同步更新缓存

@CacheEvict

缓存清除 清除的数据 依靠cacheName 和 key的值进行定位。
allEntries 清空该cacheName 中的所有缓存
beforeInvocation = false 缓存清楚是否在方法之前执行
默认是在方法之后执行,如果方法出现异常,缓存就不会清除
该值为true则是在方法之前执行。

了解了基本的Spring缓存抽象之后,就是开始整合缓存中间件了,我们常用的缓存中间件有很多,其中很多Spring也都有自动配置,上面的10个自动导入的自动配置类。
所以只要我们在依赖中导入相应的启动器,根据SpringBoot 的判定规则,他就会优先使用我们导入的中间件。

所以只要在pom文件中加入依赖即可

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

其余操作跟上述基本没有区别。也是使用@Cacheable注解。

这里需要一提的是它的序列化机制,缓存返回的结果是一个对象的时候,必须在这个对象类上继承Serializable
Serializable这个接口没有定义任何方法,仅仅作为一个标识,标志这个类可以进行序列化操作。
而SpringBoot的各个配置类中,配置的序列化器,都是JDK自带的序列化器,我们如果想要将数据序列化成json形式的,则要我们自己写序列化器,或者使用他给我们提供的工具包。
比如redis,SpringBoot给我们提供了Jackson2JsonRedisSerializer,如果想要完成对所有Object 的转化,需要设置一个ObjectMapper,对象映射,这是Jackson包中的一个工具。
下面是自定义配置类,向容器中添加这2个组件即可,一个是自定义的redis配置类,一个是自定义的RedisCacheManager

    @Bean
     public RedisCacheConfiguration redisCacheConfiguration(){
        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);

        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jackson2JsonRedisSerializer);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(300))
                .serializeValuesWith(pair);

        return redisCacheConfiguration;

     }
 	@Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        //初始化RedisCacheManager
        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration());
        return cacheManager;
    }

最后的存储结果在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值