Spring Cache缓存配置

一、pom.xml

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

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

<!-- 反射工具类用于手动扫描指定包下的注解,根据defaultCache模块增加ehcache缓存域(非Spring Cache必须)-->
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

二、Ehcache配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir" />

    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
        maxElementsOnDisk:硬盘最大缓存个数。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
    -->


    <!-- 默认缓存 -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="200000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />

</ehcache>

三、配置类

@Configuration
@EnableCaching
public class CustomConfiguration {
    
    
    /**
     * @see org.springframework.cache.interceptor.SimpleKeyGenerator
     * Generate a key based on the specified parameters.
     */
    public static Object generateKey(Object... params) {
    
    
        if (params.length == 0) {
    
    
            return SimpleKey.EMPTY;
        }
        if (params.length == 1) {
    
    
            Object param = params[0];
            if (param != null && !param.getClass().isArray()) {
    
    
                return param;
            }
        }
        return new SimpleKey(params);
    }

/**
 * 若将target作为key的一部分时,CGLIB动态代理可能导致重复缓存
 * 注意:返回的key一定要重写hashCode()和toString(),防止key对象不一致导致的缓存无法命中
 * 例如:ehcache 底层存储net.sf.ehcache.store.chm.SelectableConcurrentHashMap#containsKey
 */
    @Bean
    public KeyGenerator customKeyGenerator(){
    
    
        return (target, method, params) -> {
    
    
            final Object key = generateKey(params);
            StringBuffer buffer = new StringBuffer();
            buffer.append(method.getName());
            buffer.append("::");
            buffer.append(key.toString());
// 注意一定要转为String,否则ehcache key对象可能不一样,导致缓存无法命中
            return buffer.toString();
        };
    }


    /**
     * redis缓存管理器
     */
    @Bean
    @ConditionalOnBean(RedisConfiguration.class)
    @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "redis",
            matchIfMissing = false)
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
    
    
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(Duration.ofMinutes(10));
        return RedisCacheManager
                .builder(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(config).build();
    }


/**
 * ehcache缓存管理器(默认)
 * default XML files {@link net.sf.ehcache.config.ConfigurationFactory#parseConfiguration()}
 */
    @Bean
    @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "ehcache",
            matchIfMissing = true)
    public CacheManager ehcacheCacheManager() {
    
    
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.create();
        /**
         * 包扫描查找指定注解并将cacheNames添加到net.sf.ehcache.CacheManager(单例)
         */
        Reflections reflections = new Reflections("com.example.demo.service", new TypeAnnotationsScanner()
                , new SubTypesScanner(), new MethodAnnotationsScanner());

        Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(CacheConfig.class);
        for (Class<?> aClass : classesList) {
    
    
            final CacheConfig config = AnnotationUtils.findAnnotation(aClass, CacheConfig.class);
            if (config.cacheNames() != null && config.cacheNames().length > 0) {
    
    
                for (String cacheName : config.cacheNames()) {
    
    
                    cacheManager.addCacheIfAbsent(cacheName);
                }
            }
        }
        /**
         * 方法级别的注解 @Caching、@CacheEvict、@Cacheable、@CachePut,结合实际业务场景仅扫描@Cacheable即可
         */
        final Set<Method> methods = reflections.getMethodsAnnotatedWith(Cacheable.class);
        for (Method method : methods) {
    
    
            final Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
            if (cacheable.cacheNames() != null && cacheable.cacheNames().length > 0) {
    
    
                for (String cacheName : cacheable.cacheNames()) {
    
    
                    cacheManager.addCacheIfAbsent(cacheName);
                }
            }
        }
        EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
        ehCacheCacheManager.setCacheManager(cacheManager);
        return ehCacheCacheManager;
    }




}

四、示例

@Component
@CacheConfig(cacheNames = "XXXServiceImpl", keyGenerator = "customKeyGenerator")
public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXXEntity> implements XXXService {
    
    

    @CacheEvict(allEntries = true)
    public void evictAllEntries() {
    
    }

    @Override
    @Cacheable
    public List<XXXEntity> findById(Long id) {
    
    
        return this.baseMapper.selectList(new QueryWrapper<XXXEntity>().lambda()
                .eq(XXXEntity::getId, id));
    }

}

猜你喜欢

转载自blog.csdn.net/ory001/article/details/108547642