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序列化有关

参数

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

参考

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值