SpringCache简介

⽂档:https://spring.io/guides/gs/caching/
⾃Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache⽀持,且提供了Cache抽象提供基本的Cache抽象,⽅便切换各种底层Cache只需要更少的代码就可以完成业务数据的缓存提供事务回滚时也⾃动回滚缓存,⽀持⽐较复杂的缓存逻辑

核⼼

⼀个是Cache接⼝,缓存操作的API
⼀个是CacheManager管理各类缓存,有多个缓存

项⽬中引⼊starter依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

配置⽂件指定缓存类型

spring:
 cache:
 type: redis

启动类开启缓存注解

@EnableCaching
常⽤注解Cacheable

标记在⼀个⽅法上,也可以标记在⼀个类上缓存标注对象的返回结果,标注在⽅法上缓存该⽅法的返回值,标注在类上缓存该类所有的⽅法返回值value 缓存名称,可以有多个key 缓存的key规则,可以⽤springEL表达式,默认是⽅法参数组合
condition 缓存条件,使⽤springEL编写,返回true才缓存

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.xdclass.xdclassredis.dao.ProductMapper;
import net.xdclass.xdclassredis.model.ProductDO;
import net.xdclass.xdclassredis.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;


    @Override
    @Cacheable(value = {"product"},key = "#root.args[0]")
    public ProductDO findById(int id) {
        return productMapper.selectById(id);
    }

    @Override
    @Cacheable(value = {"product_page"},key = "#root.methodName+'_'+#page+'_'+#size")
    public Map<String, Object> page(int page, int size) {

        Page pageInfo = new Page<>(page,size);

        IPage<ProductDO> iPage = productMapper.selectPage(pageInfo,null);

        Map<String,Object> pageMap = new HashMap<>(3);

        pageMap.put("total_record",iPage.getTotal());
        pageMap.put("total_page",iPage.getPages());
        pageMap.put("current_data",iPage.getRecords());

        return pageMap;
    }

}

自定义CacheManager配置和过期时间

默认为@Primary注解所标注的CacheManager

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.time.Duration;

@Configuration
public class AppConfiguration {

    /**
     * 新的分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }


    /**
     * 1分钟过期
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager1Minute(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = instanceConfig(60L);
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }


    /**
     * 默认是1小时
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    @Primary
    public RedisCacheManager cacheManager1Hour(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = instanceConfig(3600L);
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }

    /**
     * 1天过期
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager1Day(RedisConnectionFactory connectionFactory) {

        RedisCacheConfiguration config = instanceConfig(3600 * 24L);
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }

    private RedisCacheConfiguration instanceConfig(Long ttl) {

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        // 去掉各种@JsonSerialize注解的解析
        objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
        // 只针对非空的值进行序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 将类型序列化到属性json字符串中
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(ttl))
                //.disableCachingNullValues()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));

    }

}

自定义缓存KeyGenerator
@Configuration
public class AppConfiguration {

    /**
     * 自定义缓存key规则
     * @return
     */
    @Bean
    public KeyGenerator springCacheCustomKeyGenerator() {

        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                String key = o.getClass().getSimpleName() + "_" + method.getName() + "_" + StringUtils.arrayToDelimitedString(objects, "_");
                System.out.println(key);
                return key;
            }
        };
    }

}

key 属性和keyGenerator属性只能⼆选⼀

CacheManager和keyGenerator使用

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @Cacheable(value = {"product"}, keyGenerator = "springCacheCustomKeyGenerator",cacheManager = "cacheManager1Minute")
    public ProductDO findById(int id) {
        return productMapper.selectById(id);
    }

    @Override
    @Cacheable(value = {"product_page"},keyGenerator = "springCacheCustomKeyGenerator")
    public Map<String, Object> page(int page, int size) {

        Page pageInfo = new Page<>(page,size);

        IPage<ProductDO> iPage = productMapper.selectPage(pageInfo,null);

        Map<String,Object> pageMap = new HashMap<>(3);

        pageMap.put("total_record",iPage.getTotal());
        pageMap.put("total_page",iPage.getPages());
        pageMap.put("current_data",iPage.getRecords());

        return pageMap;
    }

}

常用注解CachePut 和 CacheEvict

CachePut

根据方法的请求参数对其结果进行缓存,每次都会触发真实⽅法的调⽤value 缓存名称,可以有多个key 缓存的key规则,可以⽤springEL表达式,默认是⽅法参数组合condition 缓存条件,使⽤springEL编写,返回true才缓存

CacheEvict

从缓存中移除相应数据, 触发缓存删除的操作value 缓存名称,可以有多个@CachePut(value = {“product”},key =
“#productDO.id”)key 缓存的key规则,可以⽤springEL表达式,默认是⽅法参数组合

beforeInvocation = false
缓存的清除是否在⽅法之前执⾏ ,默认代表缓存清除操作是在⽅法执⾏之后执⾏,如果出现异常缓存就不会清除
beforeInvocation = true
代表清除缓存操作是在⽅法运⾏之前执⾏,⽆论⽅法是否出现异常,缓存都清除

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.xdclass.xdclassredis.dao.ProductMapper;
import net.xdclass.xdclassredis.model.ProductDO;
import net.xdclass.xdclassredis.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @CacheEvict(value = {"product"},key = "#root.args[0]")
    public int delById(int id) {
        return productMapper.deleteById(id);
    }

    @Override
    @CachePut(value = {"product"},key="#productDO.id", cacheManager = "cacheManager1Minute")
    public ProductDO updateById(ProductDO productDO) {
        productMapper.updateById(productDO);
        return productDO;
    }


多注解组合Caching

组合多个Cache注解使⽤,允许在同⼀⽅法上使⽤多个嵌套的@Cacheable、@CachePut和@CacheEvict注释

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.xdclass.xdclassredis.dao.ProductMapper;
import net.xdclass.xdclassredis.model.ProductDO;
import net.xdclass.xdclassredis.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @Caching(
            cacheable = {
                    @Cacheable(value = {"product"},key = "#root.args[0]"),
                    @Cacheable(value = {"product"},key = "'xdclass_'+#root.args[0]")
            },
            put = {
                    @CachePut(value = {"product_test"},key="#id", cacheManager = "cacheManager1Minute")
            }
    )
    public ProductDO findById(int id) {
        return productMapper.selectById(id);
    }

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐