Springboot缓存

目录

 

缓存

一、Springboot整合缓存

1)整合缓存步骤

2)更新缓存中数据

3)删除缓存数据

4)@CacheConfifig

二、底层原理

三、整合Redis


缓存

缓存是每一个系统都应该考虑的功能,它用于加速系统的访问,以及提速系统的性能。如:
经常访问的高频热点数据:

  • 电商网站的商品信息:每次查询数据库耗时,可以引入缓存
  • 微博阅读量、热点话题等

一、Springboot整合缓存

Spring 3.1 后定义了 org.springframework.cache.CacheManager org.springframework.cache.Cache 接口来统一不同的缓存技术
  • CacheManager:缓存管理器,用于管理各种Cache缓存组件
  • Cache 定义了缓存的各种操作, SpringCache接口下提供了各种xxxCache的实现。比如EhCacheCacheRedisCache ConcurrentMapCache等

Spring 提供了缓存注解: @EnableCaching@Cacheable@CachePut

1)整合缓存步骤

1.引入缓存 启动器

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

2.在启动类上,开启基于注解的缓存

@EnableCaching //基于注解的缓存
@MapperScan("com.mxg.springboot.mapper")
@SpringBootApplication
public class SpringBoot12CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBoot12CacheApplication.class, args);
    }
}

3.在方法上使用注解@Cacheable

属性:

  • value/cacheNames :缓存的名字
  • key : 作为缓存中的Map的 Key 值,可自已使用 SpEL 表达式指定 ( 不指定就是参数值 ),Map的value是方法的返回值
@Service
public class UserService {
    @Autowired
    UserMapper userMapper;
    /**
     * user:自定义缓存名
     * key:缓存里的Map的存储的key名,Map的value是方法返回值
     */
    @Cacheable(value = "user", key = "#id")//如果id是1,就是获取user缓存中key为1的数据
    public User getUserById(Integer id){
        return userMapper.getUserById(id);
    }
}

4.测试

打印日志,修改日志级别

#修改日志级别为debug
logging.level.com.mxg.springboot.mapper=debug

Controller层

@RestController
public class UserController {
    @Autowired
    UserService userService;
    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id")Integer id){
        return userService.getUserById(id);
    }
}

不管访问多少次,控制台可以看到只打印了一条sql语句,说明只访问了一次数据库

 

2)更新缓存中数据

通过上面获取id为1的user对象后,就会把对象放入缓存中,但是在数据库的数据被更新后,获取缓存的数据还是旧的。

@CachePut方法被调用后,将更新对应缓存中的数据(先调用方法,调完方法再将结果放到缓存

    @CachePut(cacheNames = "user", key = "#result.id")//result表示返回的数据,和#user.id一样
    public User update(User user){
        userMapper.update(user);
        return user; //返回的user会更新到缓存
    }

3)删除缓存数据

@CacheEvict:清除缓存

属性:

  • key :指要清除的数据,如 key="#id"
  • allEntries=true : 清除指定的这个缓存中所有数据,默认false
  • beforeInvocation=true : true 在方法之前执行就清除了指定key缓存;默认 false, 在方法之后执行 ,方法体 出现异常则不会清除缓存
    /**
     * allEntries=true:删除"user"缓存中map的所有数据
     */
    @CacheEvict(cacheNames = "user",key = "#id"/**,allEntries=true*/)
    public Integer delete(Integer id){
        userMapper.delete(id);
        return id;
    }

 

4)@CacheConfifig

指定缓存公共属性值,其他方法上就不需要写缓存名

@CacheConfig(cacheNames = "user") //指定公共属性值
@Service
public class UserService {
    @Autowired
    UserMapper userMapper;
    @Cacheable(key = "#id") //省略了cacheNames="user"
    public User getUserById(Integer id){
        return userMapper.getUserById(id);
    }
}

 

二、底层原理

springboot支持的缓存:

  • GenericCacheConfiguration
  • JCacheCacheConfiguration
  • EhCacheCacheConfiguration
  • HazelcastCacheConfiguration
  • InfinispanCacheConfiguration
  • CouchbaseCacheConfiguration
  • RedisCacheConfiguration
  • CaffeineCacheConfiguration
  • SimpleCacheConfiguration
  • NoOpCacheConfiguration

1.默认使用的缓存配置类:SimpleCacheConfiguration

2.SimpleCacheConfiguration使用ConcurrentMapCacheManager缓存管理器

class SimpleCacheConfiguration {
	@Bean //注入到spring容器
	ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
			CacheManagerCustomizers cacheManagerCustomizers) {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return cacheManagerCustomizers.customize(cacheManager);
	}
}

3.当发送第一次请求时,会从ConcurrentMapCacheManager管理器的cacheMap.get(name)中获取有没有ConcurrentMapCache缓存对象,如果没有则创建出来,有则直接返回

    public Cache getCache(String name) {
        //@Cacheable(cacheNames="user"),name就是"user"缓存的名字
		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); //创建ConcurrentMapCache
					this.cacheMap.put(name, cache);
				}
			}
		}
		return cache;
	}

4.接着会从ConcurrentMapCache里面调用lookup()获取缓存数据,如果有缓存数据直接响应回去,没有数据则把service方法执行的结果通过put()放入缓存

public class ConcurrentMapCache extends AbstractValueAdaptingCache {
    protected Object lookup(Object key) {
        //获取缓存数据,key是在@Cacheable(cacheNames="uesr",key="#id")指定的key
        return this.store.get(key);
    }

    //把service方法返回结果放入缓存
    public void put(Object key, @Nullable Object value) {
		this.store.put(key, toStoreValue(value));
	}
}

三、整合Redis

整合redis步骤:

1.引入redis启动器依赖

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

当我们导入了reids的启动器之后 ,springboot会采用redis作为缓存,SimpleCache缓存就不会匹配上了

2.在application全局配置文件配置redis连接地址

#默认就是localhost
spring.redis.host=127.0.0.1
#端口号
spring.redis.port=6379

3.使用RedisTemplate操作redis,参考RedisAutoConfiguration

  • StringRedisTemplate:操作字符串的
  • RedisTemplate:操作自定义的复杂类型
@SpringBootTest
class SpringBootCacheApplicationTests {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    UserMapper userMapper;
    /**
     * 五大数据类型:
         * stringRedisTemplate.opsForValue();操作字符串
         * stringRedisTemplate.opsForList();操作list
         * stringRedisTemplate.opsForHash();操作hash
         * stringRedisTemplate.opsForSet();操作set
         * stringRedisTemplate.opsForZSet();操作有序set
     */
    ///操作字符串///
    @Test
    void stringTest() {
//        stringRedisTemplate.opsForValue().set("name","zhangsan");
        String name = stringRedisTemplate.opsForValue().get("name");
        System.out.println(name);//zhangsan

//        stringRedisTemplate.opsForList().leftPushAll("list1","a","b","c");
        //获取所有元素
        List<String> strings = stringRedisTemplate.opsForList().range("list1",0,-1);
        System.out.println(strings);//[c, b, a]

//        Map<String,String> map = new HashMap<>();
//        map.put("k1","hello");
//        stringRedisTemplate.opsForHash().putAll("hash1", map);
        String k1 = (String) stringRedisTemplate.opsForHash().get("hash1","k1");
        System.out.println(k1);//hello

        stringRedisTemplate.opsForSet().add("set1","10","15","13");
        System.out.println(stringRedisTemplate.opsForSet().members("set1"));//[10, 13, 15]
    }

    操作复杂自定义类型//
    @Test
    void objectTest(){
        User user = userMapper.getUserById(1);
        //User实体类必须要序列化实现Serializable接口,否则报错
        redisTemplate.opsForValue().set("user",user);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }
}

4.修改redis默认的序列化器为json

redis默认使用jdk序列化器JdkSerializationRedisSerializer不易于我们查看

使用Json序列化器Jackson2JsonRedisSerializer:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> jsonRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}
再次测试
@SpringBootTest
class SpringBoot12CacheApplicationTests {
    @Autowired
    RedisTemplate jsonRedisTemplate;
    @Test
    void objectTest(){
        //jsonRedisTemplate.opsForValue().set("user2",userMapper.getUserById(2));
        Object o = jsonRedisTemplate.opsForValue().get("user2");
        ObjectMapper objectMapper = new ObjectMapper();
        //将Object转成User实体类
        User user2 = objectMapper.convertValue(o, User.class);
        System.out.println(user2);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值