Spring-Boot緩存


title: Spring Boot緩存
catalog: true
date: 2019-12-02 13:35:41
subtitle: SpringBoot學習
header-img: “/img/article_header/article_header.png”
tags: Spring Boot
catagories:

  • SpringBoot
    top: 1

一、JSR107

Java Caching定義了5個核心接口:

  • CachingProvider定義了創建、配置、獲取、管理何控制多個CacheingManageer,一個應用可以在運行期訪問多個CachingProvider。
  • CachingManager定義了創建、配置、獲取、管理何控制多個唯一命名的Cache,這寫Cache存在於CacheManager的上下文中,一個CacheManager僅被一個CacheProvider所擁有。
  • Cache是一個類似Map的數據結構並臨時存儲以key未索引的值,一個cache僅被一個CacheManager所擁有
  • Entry是每一個存儲在Cache的key-value對。
  • Expiry每一個存儲在Cache中的條目有一個定義的有效期,一旦超過這個時間,條目未過期的狀態。一旦過期,條目姜不可訪問、更新和刪除。緩存有效期可以通過ExpiryPolicy。

二、緩存註解

Cache 緩存接口,定義緩存操作,實現由:RedisCache、EhCacheCahe、ConcurrentMapCache
CacheManager 緩存管理器,管理各種緩存(Cache)組件
@Cacheable 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存
@CacheEvict 清空緩存
@CachePut 保證方法被調用,又希望結果被緩存
@EnableCaching 開啓基於註解的緩存
keyGenerator 緩存註解時key生成策略
serialize 緩存數據時value序列化策略

1、@Cacheable

  1. cacheNames/values:指定緩存組件的名字,將方法的返回結果放在那個緩存中,是數組的形式,可以指定多個緩存
  2. key: 緩存數據使用的key,可以用它來指定,默認是使用方法的參數值

例如:1-方法返回值 可以使用SpEL表達式 #id;參數id的值 #a0 #p0 #root.args[0]

  1. keyGenerator: key的生成器,可以自己指定key的生成器組件id key/keyGenerator:二選一使用
  2. cacheManager/cacheResolver:指定緩存管理器;或則使用cacheResolver指定獲取解析器
  3. condition:指定符合條件的情況下才緩存
  4. unless:否定緩存,當unless指定的條件爲true,方法的返回值就不會被緩存,可以獲取到結果進行判斷
  5. sync:是否使用異步模式

運行流程:

@Cacheable:

  1. 在方法運行之前,先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取,(CacheManagerz先獲取相應的緩存),第一次獲取緩存如果沒有Cache組件會自動創建

  2. 去Cache中查找緩存的內容,使用一個key,默認就是方法的參數,key是按照某種策略生成的;默認是使用keyGenerator生成的,

  3. 沒有查到緩存就調用目標方法

  4. 將目標方法返回的結果,放進緩存中

SpringEL表達式:

名字 位置 描述 示例
methodName root object 當前被調用的方法名 #root.methodName
method root object 當前被調用的方法 #root.method.name
target root object 當前被調用的目標對象 #root.target
targetClass root object 當前被調用的目標對象類 #root.targetClass
args root object 當前被調用的方法的參數列表 #root.args[0]
caches root object 當前方法調用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”})),則有兩個cache #root.caches[0].name
argument name evaluation context 方法參數的名字. 可以直接 #參數名 ,也可以使用 #p0或#a0 的形式,0代表參數的索引; #iban 、 #a0 、 #p0
result evaluation context 方法執行後的返回值(僅當方法執行之後的判斷有效,如‘unless’,’cache put’的表達式 ’cache evict’的表達式beforeInvocation=false) #result
@Cacheable(cacheNames = {"emp"}/*, keyGenerator = "myKeyGenerator"*/)
public Employee getEmp(Integer id){
    System.out.println("查詢"+id+"號員工");
    Employee emp = employeeMapper.getEmpById(id);
    return emp;
}

2、@CachePut

運行時機:

1、先調用目標方法

2、將目標方法的結果緩存起來

/**
     * @Description: 既調用方法,又更新緩存
     * 先調用方法,然後將結果進行緩存
     * key = "#employee.id"
     * key = "#result.id" 返回值的id
     * 更新和查詢用的key必須一致
     */
@CachePut(value = "emp",/*key = "#employee.id"*/key = "#result.id")
public Employee updateEmp(Employee employee){
    employeeMapper.updateEmp(employee);
    return employee;
}

3、@CacheEvict

緩存清除,可以指定key刪除

//allEntries:默認爲false,爲true時,指定清楚這個緩存中所有的數據
//beforeInvocation:緩存的清除是否在方法之前執行

/**
* @Description:@CacheEvict清除緩存
*/
@CacheEvict(value = "emp",key = "#id")
public void deleteEmp(Integer id){
employeeMapper.deleteEmpById(id);
}

4.@Caching

包含上面三個註解,適合複雜規則使用
public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
} 

例如:
    //複雜規則使用
    @Caching(
            cacheable = {
                    @Cacheable(value = "emp",key = "#lastName")
            },
            put = {
                    @CachePut(value = "emp",key = "#result.id"),
                    @CachePut(value = "emp",key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

5、@CacheConfig

指定公共屬性
@CacheConfig(cacheNames = "emp")
@Service
 public class EmployeeService {

指定key組件生成器:

@Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName()+"["+ Arrays.asList(objects).toString()+"]";
            }
        };
    }s

三、整合redis作爲緩衝

pom.xml文件添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>
  1. 引入redis的starter,容器中保存的是RedisCacheManager
  2. RedisCacheManager創建RedisCache來作爲緩衝組件
  3. 默認保存數據k-v都是object,利用序列化保存
@SpringBootTest
class Springboot01CacheApplicationTests {

    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedisTemplate<Object,Employee> employeeRedisTemplate;

    /*
     *Redis常見五種數據類型: String、List、Set、Hash(散列)、ZSet(有序集合)
     *stringRedisTemplate.opsForValue()操作String;
     *stringRedisTemplate.opsForList()操作List;
     * stringRedisTemplate.opsForSet()操作Set;
     * stringRedisTemplate.opsForHash()操作Hash;
     * stringRedisTemplate.opsForZSet()操作Zset;
     */
    @Test
    public void test01(){
//        stringRedisTemplate.opsForValue().append("msg","hello");
        stringRedisTemplate.opsForList().leftPush("myList","1");
        stringRedisTemplate.opsForList().leftPush("myList","2");
        String msg = stringRedisTemplate.opsForValue().get("msg");
        System.out.println("msg = " + msg);
//        stringRedisTemplate.opsForList();
//        stringRedisTemplate.opsForSet();
//        stringRedisTemplate.opsForHash();
//        stringRedisTemplate.opsForZSet();
//        System.out.println("=========");
    }

    @Test
    public void test02(){
        Employee emp = employeeMapper.getEmpById(1);
        employeeRedisTemplate.opsForValue().set("emp-01",emp);
    }

    @Test
    void contextLoads() {
        Employee emp = employeeMapper.getEmpById(1);
    }
}

​ 自定義cachemanager序列化爲json

@Bean
public CacheManager 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 configuration = RedisCacheConfiguration.defaultCacheConfig()
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
    RedisCacheManager build = RedisCacheManager.builder(factory).cacheDefaults(configuration).build();
    return build;
}

獲取緩存並添加緩存

@Autowired
CacheManager cacheManager;

@Cacheable(cacheNames = "dept")
public Department getDeptById(Integer id){
    System.out.println("查詢部門" + id);
    Department dept = departmentMapper.getDeptByid(id);
    Cache dept1 = cacheManager.getCache("dept");
    dept1.put("dept1",dept);
    return dept;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章