Skip to content

Spring Boot中使用Redis实现缓存

sqmax edited this page Jan 1, 2019 · 3 revisions

在大流量的场景下,缓存可以有效地提高数据的读取速度。缓存一般会遇到以下三个概念:

  • 命中:指应用程序从cache里获取数据,取到后返回。
  • 失效:缓存时间到了,缓存也就失效了。
  • 更新:应用程序把数据存到数据库中,再放回缓存里面去。

基于注解实现缓存主要有一下三个注解:
@Cacheable,@CachePut,@CacheEvict

使用缓存时,注意被缓存的对象必须是可序列化的。

下面介绍使用注解实现Redis缓存的的具体过程。

  1. 在启动类上加注解@EnableCaching
@SpringBootApplication
@EnableCaching
public class SellApplication {
	public static void main(String[] args) {
		SpringApplication.run(SellApplication.class, args);
	}
}
  1. 在需要缓存的返回给前端数据的Controller方法上@Cacheable注解,第一次访问该注解的下的方法时,数据会被缓存,下次再访问该接口时就会直接从缓存中读取。
    @GetMapping("/list")
    @Cacheable(cacheNames = "product",key = "123")
    public ResultVO list(){

        //1.查询所有的上架的商品
        List<ProductInfo> productInfoList=productService.findUpAll();

        //2.查询在架商品所属类目(一次性查询)
//        List<Integer> categoryTypeList=new ArrayList<>();
//        //传统方法
//        for(ProductInfo productInfo: productInfoList){
//            categoryTypeList.add(productInfo.getCategoryType());
//        }
        //精简方法lamba表达式
        List<Integer> categoryTypeList=productInfoList.stream()
                .map(e->e.getCategoryType()).collect(Collectors.toList());

        List<ProductCategory> productCategoryList=categoryService.findByCategoryTypeIn(categoryTypeList);

        //3. 数据拼装
        List<ProductVO> productVOList=new ArrayList<>();
        for(ProductCategory productCategory: productCategoryList){
            ProductVO productVO=new ProductVO();
            productVO.setCategoryName(productCategory.getCategoryName());
            productVO.setCategoryType(productCategory.getCategoryType());

            List<ProductInfoVO> productInfoVOList=new ArrayList<>();
            for(ProductInfo productInfo: productInfoList){
                if(productInfo.getCategoryType().equals(productCategory.getCategoryType())){
                    ProductInfoVO productInfoVO=new ProductInfoVO();
                    BeanUtils.copyProperties(productInfo,productInfoVO);
                    productInfoVOList.add(productInfoVO);
                }
            }
            productVO.setProductInfoVOList(productInfoVOList);
            productVOList.add(productVO);
        }

//        ResultVO resultVO=new ResultVO();
//        resultVO.setData(productVOList);
//        resultVO.setCode(0);
//        resultVO.setMsg("成功");
        ResultVO resultVO=ResultVOUtil.success(productVOList);
        return resultVO;
    }
  1. 修改该数据的方法上需要加上@CachePut(cacheNames = "product",key = "123")注解,每次访问该注解下的方法,返回的数据会被写入redis当中,数据必须是可序列化的。但有时我们的方法返回的不是自定义类型,这时我们可以用@CacheEvict(cacheNames="product",key="123)注解,访问该注解下的方法之后,redis会把缓存的数据清除掉。如下面修改商品信息的方法必须用@CacheEvict(cacheNames="product",key="123)注解。
/**
     * 保存/更新
     * @param form
     * @param bindingResult
     * @return
     */
    @PostMapping("/save")
    //@CachePut(cacheNames = "product",key = "123")
    @CacheEvict(cacheNames="product",key="123)
    public ModelAndView save(@Valid ProductForm form,
                             BindingResult bindingResult,
                             Map<String,Object> map){
        if(bindingResult.hasErrors()){
            map.put("msg",bindingResult.getFieldError().getDefaultMessage());
            map.put("url","sell/seller/product/index");
            return new ModelAndView("common/error",map);
        }

        ProductInfo productInfo=new ProductInfo();
        try{
            //如果productId不为空,说明是已有商品
            if(!StringUtils.isEmpty(form.getProductId())){
                productInfo=productService.findOne(form.getProductId());
            }else{
                form.setProductId(KeyUtil.genUniqueKey());
            }
            BeanUtils.copyProperties(form,productInfo);
            productService.save(productInfo);
        }catch (SellException e){
            map.put("msg",e.getMessage());
            map.put("url","/sell/seller/product/index");
            return new ModelAndView("common/error",map);
        }
        map.put("url","/sell/seller/product/list");
        return new ModelAndView("common/success",map);
    }

如果非要想用@CachePut(cacheNames = "product" ,key = "123")这个注解,我们可以将注解放到Service方法上,因为Service方法一般返回的都是自定义的实体对象。

注意:key这个参数要写,若不写,key的值就是方法的参数的值。

@Override
    @Cacheable(cacheNames = "product",key = "123")
    public ProductInfo findOne(String productId) {
        return repository.findOne(productId);
    }

    @Override
    @CachePut(cacheNames = "product" ,key = "123")
    public ProductInfo save(ProductInfo productInfo) {
        return repository.save(productInfo);
    }

如果一个类的多个方法cacheNames这个参数相同,那么可以在该类上加上@CacheConfig注解,方法上就不必再写该参数了。

@Service
@CacheConfig(cacheNames = "product")
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductInfoRepository repository;

    @Override
    @Cacheable(key = "1234")
    public ProductInfo findOne(String productId) {
        return repository.findOne(productId);
    }

    @Override
    @CachePut(key = "1234")
    public ProductInfo save(ProductInfo productInfo) {
        return repository.save(productInfo);
    }
}

注解@Cacheable的参数key的值可以为SpEL表达式,还可以有condition参数、unless参数,表示缓存的条件。

@Cacheable(cacheNames="product",key="#sellerId,condition="#sellerId.length()>3unless="#result.getCode()!=0")
public ResultVO list(@RequestParam("sellerId") String sellerId){
......
}