SpringBoot的Cacheable缓存入门

Posted a10615

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot的Cacheable缓存入门相关的知识,希望对你有一定的参考价值。

因为工作需要,最近开始学习SpringBoot。要使用缓存,就搜索了下SpringBoot的缓存方案,有自带的ConcurrentLinkedHashMap,还有主流的GuavaCaffeineEhcache

作为入门,先拿自身的来玩玩,也就是ConcurrentLinkedHashMap

一、ConcurrentLinkedHashMap的使用

先简单说一下,此缓存保存在内存中,如果关闭或重启服务,缓存将被清除。如果要长久保存,可以结合Redis

使用方式,很简单,抓住这几个注解关键词:@EnableCaching、@Cacheable、@CachePut、@CacheEvict

1.1 @EnableCaching

一般在启动基类上加上此注解,表示打开缓存功能

@SpringBootApplication
@EnableCaching // 打开缓存功能
public class DemoApplication 

1.2 @Cacheable

用在获取并缓存数据的方法上,表示此方法有缓存功能。当调用此方法时,会先从缓存中查找

  • 如果缓存里有,就直接返回,不会运行方法内部语句;
  • 如果没有,则执行方法内部语句,然后把返回值缓存起来
/** 临时用HashMap当作数据库 */
private HashMap<Integer, UserBean> users = new HashMap<>();

@Cacheable(cacheNames = "cacheModel", key = "#id", unless = "#result == null")
public UserBean getById(int id) 
    System.out.println("CacheModel.getById:id=" + id);
    UserBean user = new UserBean(id, "name_" + new Random().nextInt(100));
    users.put(id, user);
    return user;

1.3 @CachePut

用在更新缓存的方法上,表示更新缓存中的数据。

@CachePut(cacheNames = "cacheModel", key = "#user.id", unless = "#user == null")
public UserBean updateUser(UserBean user)
    System.out.println("CacheModel.updateUser: user=" + user);
    return users.put(user.getId(), user);

注意:更新缓存时,如果缓存里没有,会把key=user.id,value=null给缓存。所以最好在调用此方法前先确认缓存已存在,否则更新后再获取值为null

1.4 @CacheEvict

用在删除缓存的方法上,表示根据条件删除对应的缓存

@CacheEvict(cacheNames = "cacheModel", key = "#id")
public UserBean removeUser(int id) 
    System.out.println("CacheModel.removeUser: id=" + id);
    return users.remove(id);

如果要清除cacheNames指定名称的所有缓存,增加参数:allEntries = true

@CacheEvict(cacheNames = "cacheModel", allEntries = true)
public void clear() 
    System.out.println("CacheModel.clear all");

二、应用

2.1 Bean

@Data
@ToString
@NoArgsConstructor
public class UserBean 
    private int id;
    private String name;

    public UserBean(int id, String name) 
        this.id = id;
        this.name = name;
    

2.2 RestController

@RestController
@RequestMapping("/user")
public class UserController 

    @Autowired
    private CacheModel model;

    @GetMapping("/getById")
    public ResponseEntity<UserBean> getById(@RequestParam("id") int id) 
        UserBean user = model.getById(id);
        return new ResponseEntity<>(user, HttpStatus.OK);
    

    @PutMapping("/update/id")
    public ResponseEntity<UserBean> updateUser(@PathVariable("id") int id,
                                               @RequestParam("name") String name) 
        UserBean user = new UserBean(id, name);
        UserBean result = model.updateUser(user);
        System.out.println("UserController.updateUser: result =" + result);
        return new ResponseEntity<>(user, HttpStatus.OK);
    

    @PutMapping("/update")
    public ResponseEntity<UserBean> updateUser(@RequestBody UserBean user) 
        UserBean result = model.updateUser(user);
        System.out.println("UserController.updateUser: result =" + result);
        return new ResponseEntity<>(user, HttpStatus.OK);
    

    @DeleteMapping("/del")
    public ResponseEntity<UserBean> delete(@RequestParam("id") int id) 
        UserBean user = model.removeUser(id);
        return new ResponseEntity<>(user, HttpStatus.OK);
    

    @PostMapping("/clear")
    public ResponseEntity<String> clear() 
        model.clear();
        return new ResponseEntity<>("success", HttpStatus.OK);
    

2.3 测试

Windows下测试(Windows系统里,不能使用单引号',双引号"里的引用必须用三个双引号"""

> curl http://localhost:8080/user/getById?id=11

> curl http://localhost:8080/user/update/16?name=name_16 -X PUT
# 或
> curl http://localhost:8080/user/update -X PUT -H "Content-Type:application/json" -d """"id""":11, """name""":"""name_json"""" 

> curl http://localhost:8080/user/del?id=11 -X DELETE
> curl http://localhost:8080/user/clear -X DELETE

三、无效原因

3.1 cacheNames无效

在application.properties中已经配置好了缓存名称,如果cacheNames的值没包含在配置中,就会报错无效

spring.cache.cache-names=testModel, cacheModel

3.2 未被Spring代理

这是最最重要的一点,因为@Cacheable是Spring框架下的,如果未通过cglib生成代理,直接调用new对象出来的方法,就不会缓存了。一开始接触Spring,未理解Spring的AOP,直接写了个单例,结果就无效了

所以,如果遇到无效的情况,依次检查:

  • 是否被Spring代理了。通过对象的名称来判断model.getClass().getName(),正确类似这种格式:CacheModel$$EnhancerBySpringCGLIB$$3b28ee6d
  • 是否直接调用了类内部的缓存方法。在类的内部,方法A调用缓存方法B,是不会走动态代理的,所以也无效,如下。缓存方法必须通过外部来调用才有效
    /**
     * 调用内部方法,缓存无效
     */
    public UserBean getUser(int id) 
        return getById(id);
    
    
    @Cacheable(cacheNames = "cacheModel", key = "#id", unless = "#result == null")
    public UserBean getById(int id) 
    	...
    
    

有的还可能跟缓存数据对象的Serializable序列化有关

参数

参数说明,请参考其它文章

参考

以上是关于SpringBoot的Cacheable缓存入门的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot的Cacheable缓存入门

Springboot @Cacheable缓存过期时间

SpringBoot的Cacheable缓存问题一则

SpringBoot的Cacheable缓存问题一则

SpringBoot的Cacheable缓存问题一则

Springboot集成Redis详细教程(缓存注解使用@Cacheable,@CacheEvict,@CachePut)