SpringBoot与缓存

几个重要的概念 & 缓存注解

Cache

缓存接口,定义缓存操作。实现有:RedisCacheEhCacheCacheConcurrentMapCache

CacheManager

缓存管理器,管理各种缓存(Cache)组件

@Cacheable

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

@CacheEvict

清空缓存

@CachePut

保证方法调用,又能将结果缓存,可以用于刷新缓存

@EnableCaching

开启基于注解的缓存

keyGenerator

缓存数据时key生成策略

serialize

缓存数据时value序列化的策略

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

使用缓存

首先引入cache组件

@Cacheable

将方法的运行结果进行缓存,以后要相同的数据,直接从缓存中获取,不用调用方法

  • CacheManager管理多个Cache组件的,每一个缓存组件有自己唯一的名字
  • 几个属性:
    • cacheNames/value:指定缓存组件的名字 cacheNames = {"emp"} 
    • key:缓存数据使用的key,默认使用的是方法的参数、还可以使用SpEL来编写
    • keyGeneratorkey的生成器,生成缓存数据的keykeyGeneratorkey只能二选一
    • cacheManager:指定缓存管理器,或者cacheResolver获取解析器
    • condition:指定符合条件的情况下才缓存。例:condition="#id>0",id大于0才缓存 
    • unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以用#result获取方法的返回值。例: unless = "#result == null" 
    • sync: 是否使用异步模式

key的取值

名字

描述

示例

methodName

当前被调用的方法名

#root.methodName

method

当前被调用的方法

#root.method.name

target

当前被调用的目标对象

#root.target

targetClass

当前被调用的目标对象类

#root.targetClass

args

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

#root.args[0]

caches

当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache

#root.caches[0].name

argument name

方法参数的名字,可以直接 #参数名,也可以使用 #p0#a0 的形式,0代表参数的索引

#iban#a0, #p0

result

方法执行后的返回值(仅当前方法执行之后的判断有效,如’unless’, ‘cache put’的表达式 ‘cache evict’的表达式beforeInvocation=false

#result

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

例:

①、首先在主程序类上标注上@EnableCaching注解

@EnableCaching
@SpringBootApplication
public class SpringBootCacheApplication {

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

②、在要缓存的方法上标注即可

@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
    System.out.println("查询" + id + "员工号");
    Employee emp = employeeMapper.getEmployeeById(id);
    return emp;
}

@CachePut

这个注解用于更新缓存

示例:

/**
 * @CachePut: 既调用方法,又更新缓存
 * 主要用于修改了数据库的某个数据,同时更新缓存
 * 运行时机:先调用目标方法,再将目标方法的结果缓存起来
 * 与@Cacheable不同,@Cacheable是先去缓存里面查看是否有值,再执行方法,因而这里在获取id的时候@CachePut可以通过#result获取而@Cacleable不行
 * 注意:这里的key一定要填要更新的缓存的key,不填就会默认使用第一个参数作为key,很可能就达不到更新缓存的目的而是新创建了一个缓存
 */
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee){
    System.out.println("更新cache");
    employeeMapper.updateEmp(employee);
    return employee;
}

@CacheEvict

该注解用于删除一个缓存

示例:

/**
 * allEntries:删除所有的缓存   allEntries = true
 * beforeInvocation:缓存的清除是否在方法之前执行,默认是在方法之后执行
 * 其他参数与@Cacheable大致一样
 * @param id
 */
@CacheEvict(value = "emp", key = "#id", allEntries = true)
public void deleteEmp(Integer id){
    System.out.println("删除" + id);
}

@Caching

该注解用于存放多个缓存注解,有时候对于一个方法,想放置多个缓存,既想缓存又想更新时使用

示例:

/**
 * 可以放多个注解
 * @param lastName
 * @return
 */
@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){
    System.out.println("查询");
    return employeeMapper.getEmpByLastName(lastName);
}

@CacheConfig

有的时候觉得每次在使用缓存注解的时候都要指定缓存的名字,或者指定缓存的cacheManager之类的,觉得很麻烦。那么就可以在类上使用@CacheConfig统一的配置缓存的名字

@CacheConfig(value = "emp")
@Service
public class EmployeeService {

自定义key的生成策略

对于key,我们可以让它自动生成,生成的策略可以有我们自己制定,之需要在配置类中将我们的定制规则加入到容器中即可

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

使用Redis做为缓存

SpringBoot默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache,将数据保存在ConcurrentMap<String, Cache> cacheMap里面

当然我们还可以使用其他的中间件作为缓存

 

开发中使用的缓存中间件:redismemcachedehcache

引入redis的starter

<!-- 引入redis的starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置redis

来到配置文件application.yaml配置redis的主机地址

spring:
  redis:
    host: 172.17.119.176

使用redis

springbootstarter提供了两个Template操作redis

StringRedisTemplateRedisTemplate,其中StringRedisTemplatekeyvalue都是字符串。redisTemplatekeyvalue都是Object

/**
 * String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)、HyperLogLog
 * stringRedisTemplate.opsForValue()  操作String(字符串)
 * stringRedisTemplate.opsForList()   操作List(列表)
 * stringRedisTemplate.opsForSet()    操作Set(集合)
 * stringRedisTemplate.opsForHash()   操作Hash(散列)
 * stringRedisTemplate.opsForZSet()   操作ZSet(有序集合)
 * stringRedisTemplate.opsForHyperLogLog()  操作HyperLogLog
 */
@Test
public void test01(){
    stringRedisTemplate.opsForValue().append("redis", "hello");
    stringRedisTemplate.opsForList();
    stringRedisTemplate.opsForSet();
    stringRedisTemplate.opsForHash();
    stringRedisTemplate.opsForZSet();
    stringRedisTemplate.opsForHyperLogLog();
}

储存对象

有的时候需要将对象存进redis(例如一个JavaBean对象),但是如果对象不是可Serializable的,因此需要让JavaBean对象实现Serializable接口

public class Employee implements Serializable {

序列化机制

如果只是让JavaBean实现Serializable接口也是可以存储的,但是并不好看,那么能不能将JavaBean弄成Json的样式放进redis呢。直接的方式就是自己转换,但是未免有点麻烦,那就只能修改RedisTemplate的序列化机制了,在配置类中配置上序列化的方法即可

@Bean
public RedisTemplate<Object, Employee> empredisTemplate(
        RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException {
    RedisTemplate<Object, Employee> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
    redisTemplate.setDefaultSerializer(ser);
    return redisTemplate;
}

自定义CacheManager

对于自定义CacheManager,SpringBoot 1.x跟SpringBoot 2.x有所不同

SpringBoot 1.x

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
    //默认超时时间,单位秒
    redisCacheManager.setDefaultExpiration(60);
    //缓存超时时间Map,key为cacheName,value为超时,单位是秒
    Map<String, Long> expiresMap = new HashMap<>();
    //缓存用户信息的cacheName和超时时间
    expiresMap.put("user", 1800L);
    //缓存产品信息的cacheName和超时时间
    expiresMap.put("product", 600L);
    redisCacheManager.setExpires(expiresMap);
    return redisCacheManager;
}

SpringBoot 2.x

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
    return RedisCacheManager
            .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
            .cacheDefaults(redisCacheConfiguration).build();
}

编码的方式使用缓存

前面都是用的注解的方法将注解标注在方法上去缓存方法的结果,但是如果在方法的执行中有一个数据我们想缓存那应该怎么办呢?既然前面提到过CacheManager是管理Cache的,那我们就可以直接使用CacheManager去缓存了

@Autowired
RedisCacheManager redisCacheManager;

public void test(){
    Cache emp = redisCacheManager.getCache("emp");
    emp.put("111", "222");
}

  

 

posted @ 2018-09-19 10:37  Jin同学  阅读(4018)  评论(0编辑  收藏  举报