专栏首页波波烤鸭SpringCache完整案例介绍

SpringCache完整案例介绍

  Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager 接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;Cache接口下Spring提供了各种xxxCache的实现;如RedisCacheEhCacheCache ,ConcurrentMapCache等;本文我们就来介绍下SpringCache的具体使用。

一、缓存中的重要概念

注解

说明

Cache

缓存接口,定义缓存操作。实现有:RedisCache、EhcacheCache、ConcurrentMapCache等

CacheManager

缓存管理器,管理各种缓存组件

@Cacheable

注意针对方法配置,能够根据方法的请求参数对其进行缓存

@CacheEvict

清空缓存

@CachePut

保证方法被调用,又希望结果被缓存。与@Cacheable区别在于是否每次都调用方法,常用于更新

@EnableCaching

开启基于注解的缓存

keyGenerator

缓存数据时key生成策略

serialize

缓存数据时value序列化策略

@CacheConfig

统一配置类的缓存注解属性

@Cacheable/@CachePut/@CacheEvict 主要的参数

属性

说明

value

缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}

key

缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:@Cacheable(value=”testcache”,key=”#id”)

condition

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

unless

否定缓存。当条件结果为TRUE时,就不会缓存@Cacheable(value=”testcache”,unless=”#userName.length()>2”)

allEntries (@CacheEvict )

是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如: @CachEvict(value=”testcache”,allEntries=true)

beforeInvocation(@CacheEvict)

是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存例如:@CachEvict(value=”testcache”,beforeInvocation=true)

二、SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档

名称

位置

描述

示例

methodName

root对象

当前被调用的方法名

#root.methodname

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象实例

#root.target

targetClass

root对象

当前被调用的目标对象的类

#root.targetClass

args

root对象

当前被调用的方法的参数列表

#root.args[0]

caches

root对象

当前方法调用使用的缓存列表

#root.caches[0].name

Argument Name

执行上下文

当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数

#artsian.id

result

执行上下文

方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false)

#result

注意

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

三、SpringCache的使用

1.导入依赖

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

2.然后在启动类注解@EnableCaching开启缓存

3.创建业务类

package com.dpb.springboot.service;

import com.dpb.springboot.pojo.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @program: springboot-13-cache
 * @description:
 * @author: 波波烤鸭
 * @create: 2019-11-27 21:25
 */
@Service
public class UserService {

    /**
     * @Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存
     * 此处的User实体类一定要实现序列化public class User implements Serializable,否则会报java.io.NotSerializableException异常。
     * @param userName
     * @return
     */
    @Cacheable(value = "userCache" , key = "#userName")
    public User getUserByName(String userName){
        System.out.println("数据库查询...." + userName);
        return getFromDB(userName);
    }

    /**
     * 清除一条记录
     * @param user
     */
    @CacheEvict(value = "userCache",key = "#user.name")
    public void updateUser(User user){
        System.out.println("数据更新了。。。。数据库");
        updateDB(user);
    }

    /**
     * allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
     * beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,
     *          则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存
     */
    @CacheEvict(value = "userCache",allEntries = true,beforeInvocation = true)
    public void reload(){
        //
    }

    private User getFromDB(String userName){
        System.out.println("查询数据库..." + userName);
        return new User(666,userName);
    }

    private void updateDB(User user){
        System.out.println("更新数据..." + user.getName());
    }
}

4.创建缓存实现类

  我们自定义一个基于内存的缓存实现 Cache接口,并实现相关的方法

package com.dpb.springboot.cache;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: springboot-13-cache
 * @description:
 * @author: 波波烤鸭
 * @create: 2019-11-27 21:32
 */
public class MyCache implements Cache {
    // 缓存的 key
    private String name;

    // 保存缓存数据的容器
    private Map<String,Object> store = new ConcurrentHashMap<>();

    public MyCache() {
    }

    public MyCache(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 返回数据容器
     * @return
     */
    @Override
    public Object getNativeCache() {
        return store;
    }

    /**
     * 返回缓存数据
     * @param key
     * @return
     */
    @Override
    public ValueWrapper get(Object key) {
        ValueWrapper result = null;
        Object thevalue = store.get(key);

        if(thevalue != null){
            result = new SimpleValueWrapper(thevalue);
            System.out.println("执行了缓存查询...命中" + key);
        }else{
            System.out.println("执行了缓存查询...没有命中" + key);
        }
        return result;
    }

    /**
     * 返回缓存数据  基于泛型
     * @param key
     * @param aClass
     * @param <T>
     * @return
     */
    @Override
    public <T> T get(Object key, Class<T> aClass) {
        return aClass.cast(store.get(key));
    }

    @Override
    public <T> T get(Object o, Callable<T> callable) {
        return null;
    }


    /**
     * 保存缓存数据
     * @param o
     * @param o1
     */
    @Override
    public void put(Object o, Object o1) {
        //
        System.out.println("数据缓存了..." + o);
        store.put((String)o,o1);
    }

    /**
     * 清除一条缓存数据
     * @param key
     */
    @Override
    public void evict(Object key) {
        System.out.println("移走了元素:" + key);
        store.remove(key);
    }

    /**
     * 清空所有的数据
     */
    @Override
    public void clear() {
        store.clear();
    }
}

5.配置缓存管理器

Spring的配置文件中如下配置

6.测试代码

package com.dpb.springboot;

import com.dpb.springboot.pojo.User;
import com.dpb.springboot.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Springboot13CacheApplicationTests {

    @Autowired
    private UserService service ;

    @Test
    void contextLoads() {
        System.out.println("第一次查询...");
        service.getUserByName("hello");
        System.out.println("第二次查询...");
        service.getUserByName("hello");
        System.out.println("*************");

        // 更新记录
        User user1 = service.getUserByName("user1");
        // 开始更新其中一个
        user1.setId(1111);
        service.updateUser(user1);
        // 更新后再查询
        service.getUserByName("user1");
        // 再次查询
        service.getUserByName("user1");
        // 更新所有
        service.reload();
        System.out.println("清空了所有的缓存...");

        service.getUserByName("user1");
        service.getUserByName("user2");

        service.getUserByName("user1");
        service.getUserByName("user2");
    }

}

测试结果

第一次查询...
执行了缓存查询...没有命中hello
数据库查询....hello
查询数据库...hello
数据缓存了...hello
第二次查询...
执行了缓存查询...命中hello
*************
执行了缓存查询...没有命中user1
数据库查询....user1
查询数据库...user1
数据缓存了...user1
数据更新了。。。。数据库
更新数据...user1
移走了元素:user1
执行了缓存查询...没有命中user1
数据库查询....user1
查询数据库...user1
数据缓存了...user1
执行了缓存查询...命中user1
清空了所有的缓存...
执行了缓存查询...没有命中user1
数据库查询....user1
查询数据库...user1
数据缓存了...user1
执行了缓存查询...没有命中user2
数据库查询....user2
查询数据库...user2
数据缓存了...user2
执行了缓存查询...命中user1
执行了缓存查询...命中user2

搞定

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Redis面试题汇总

      redis中的五种常用类型分别是string,Hash,List,Set,ZSet。

    用户4919348
  • shiro教程8(缓存管理)

      在没有使用缓存的情况下,我们每次发送请求都会调用一次doGetAuthorizationInfo方法来进行用户的授权操作,但是我们知道,一个用户具有的权限一...

    用户4919348
  • Redis应用之缓存实现

      Redis的众多应用场景中缓存绝对是频率最高的场景了。本文来介绍下Redis作为缓存要注意的地方。

    用户4919348
  • SpringBoot2.0 基础案例(13):基于Cache注解模式,管理Redis缓存

    从Spring3开始定义Cache和CacheManager接口来统一不同的缓存技术; Cache接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache...

    知了一笑
  • 负载均衡环境下缓存处理

    深入学习Enterprise Library for .NET Framework 2.0的Cache机制——分析篇, 这篇文章介绍了很多Caching方面的...

    张善友
  • 缓存穿透,缓存击穿,缓存雪崩的内容和解决方案

    每次想到缓存的概念时就会想到下面这张结构图,缓存主要解决的是中央处理器与内存之间速度不匹配出来的问题。

    后端Coder
  • 缓存的Cache Aside模式

    codecraft
  • Guava CacheBuilder使用说明 原

    CacheBuilder是Guava用于创建LoadingCache、Cache实例的构建类。可以使用下面的方法来创建一个Cache实例。

    随风溜达的向日葵
  • 大型分布式网站架构:缓存在分布式系统中的应用

    缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。

    烂猪皮
  • 本地缓存实现 需要考虑几个方面

    实现本地缓存,存储容器肯定是 key/value 形式的数据结构,在 Java 中,也就是我们常用的 Map 集合。Map 中有 HashMap、Hashtab...

    海涛

扫码关注云+社区

领取腾讯云代金券