SpringBoot第一谈:缓存的快速使用

252 阅读11分钟

SpringBoot 缓存

在Spring Boot中通过@EnableCaching注解自动化配置合适的缓存管理器。当我们不指定具体其他第三方实现的时候,Spring Boot的Cache模块会使用ConcurrentHashMap来存储。而实际生产使用的时候,因为我们可能需要更多其他特性,往往就会采用其他缓存框架,所以接下来介绍常用缓存的整合与使用。

1、引入缓存

第一步:在pom.xml中引入cache依赖,添加如下内容:

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

第二步pom.xml中增加相关依赖::在Spring Boot主类中增加@EnableCaching注解开启缓存功能,如下:

@EnableCaching
@SpringBootApplication
public class Chapter51Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter51Application.class, args);
    }

}

第三步pom.xml中增加相关依赖::在数据访问接口中,增加缓存配置注解,如:

public interface UserService extends BaseService<User>{

    /**
     * 获取用户列表
     * @param name 名称
     * @param age 年龄
     * @return
     */
    @Cacheable(cacheNames = "test", key = "#name+'_'+#age")
    List<User> listUser(String name, Integer age);

}

第四步:执行请求,可以在控制台中输出了下面的内容

2021-12-08 10:26:18.821 TRACE 43986 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.cache.concurrent.ConcurrentMapCacheManager
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where (user0_.name=? or ''=?) and (user0_.age=? or ? is null) order by user0_.id

2021-12-08 10:26:18.821 TRACE 43986 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.cache.concurrent.ConcurrentMapCacheManager

到这里,我们可以看到,在调用第二次listUser函数时,没有再执行select语句,也就直接减少了一次数据库的读取操作。

关于@Cacheable相关详细配置,可自行百度

    @Cacheable注解

    @Cacheable注解也是有Spring框架提供的,可以作用域类或者方法上(通常用在数据查询方法上),用于对方法结果进行缓存存储。

    @Cacheable注解执行顺序是,先进行缓存查询,如果为空进行方法查询,并将结果进行缓存;如果缓存中有数据,不进行方法查询,直接使用缓存数据。

属性名说明
value/cacheNames指定缓存空间的名称,必配属性。两个属性二选一使用
key指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式
keyGenerator指定缓存数据的key的生成器,与key属性二选一使用
cacheManager指定缓存管理器
cacheResolver指定缓存解析器,与cacheManager属性二选一使用
condition指定在符合某条件下,进行数据缓存
unless指定在符合某条件下,不进行数据缓存
sync指定是否使用异步缓存,默认为false

    @CachePut注解

    @CachePut注解有Spring框架提供,可以用于类或者方法上(通常用在数据更新方法上),该注解的作用是更新缓存数据。@CachePut注解的执行顺序是,先进行方法调用,然后将方法结果更新到缓存中。

    @CachePut注解也提供了和@Cacheable相同的注解属性。

    @CacheEvict注解

    @CacheEvict注解有Spring框架提供,可以用于类或者方法上(通常用在数据删除方法上),该注解的作用是删除缓存数据。@CacheEvict注解的执行顺序是,先进行方法调用,然后将缓存进行删除。

     @CacheEvict注解除了拥有和@Cacheable相同的注解属性外,还额外提供了两个特殊注解allEntries和beforeInvocation。

属性名说明
allEntries表示是否清除指定缓存控件的所有缓存数据,默认为false
beforeInvocation表示是否在方法执行前进行缓存清除,默认为false

    @Caching注解

    @Caching注解用于针对复杂规则的数据缓存管理,可以用于类或者方法,在@Caching注解内部包含Cacheable、put、evict三个属性,分别对应于@Cacheable、@CachePut、@CacheEVict三个注解。

    @CacheConfig注解

    @CacheConfig注解使用在类上,主要用于统筹管理类中所有使用@Cacheable、@CachePut、@CacheEvict注解标注方法中的公共属性,这型公共属性包括cacheNames、keyGeneragor、cacheManager、cacheResolver。

2、使用EhCache缓存

2-1 快速集成

第一步:在pom.xml中引入ehcache依赖

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

第二步:在src/main/resources/cache目录下创建:ehcache-spring.xml

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

    <!--
        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:内存数量最大时是否清除。
    -->

    <diskStore path="java.io.tmpdir"/>

    <!-- 默认缓存 -->
    <defaultCache name="defaultCache"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="600"
        timeToLiveSeconds="600"
        overflowToDisk="false"
        maxElementsOnDisk="100000"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU"/>

    <!-- 系统缓存 -->
    <cache name="sysCache"
        maxElementsInMemory="10000"
        maxElementsOnDisk="100000"
        eternal="false"
        timeToIdleSeconds="86400"
        timeToLiveSeconds="86400"
        overflowToDisk="false"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU"/>

</ehcache>

第三步:在application.properties配置如下

##ehcache
spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:cache/ehcache-spring.xml

第四步:执行请求,可以在控制台中输出了下面的内容

2021-12-08 10:26:18.821 TRACE 43986 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.cache.ehcache.EhCacheCacheManager
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where (user0_.name=? or ''=?) and (user0_.age=? or ? is null) order by user0_.id

2021-12-08 10:27:20.486 TRACE 43986 --- [nio-8888-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.cache.ehcache.EhCacheCacheManager

可以看到:

  1. 第一行输出的CacheManager type为org.springframework.cache.ehcache.EhCacheCacheManager,而不是上一篇中的ConcurrentHashMap了。
  2. 第二次查询的时候,没有输出SQL语句,所以是走的缓存获取

2-2 EhCache配置类

/**
 * @title EhCacheConfig
 * @description EhCache配置类
 * @author zxkxc
 * @date 2023年02月06日
 */
@Configuration
@EnableCaching
public class EhCacheConfig extends CachingConfigurerSupport {

    /**
     * EhCache的配置
     */
    @Bean
    public EhCacheCacheManager cacheManager(CacheManager cacheManager) {
        return new EhCacheCacheManager(cacheManager);
    }

    /**
     * EhCache的配置
     */
    @Bean
    public EhCacheManagerFactoryBean ehcache() {
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("cache/ehcache-spring.xml"));
        return ehCacheManagerFactoryBean;
    }
}

2-3 EhCache工具类

/**
 * @title EhcacheUtil
 * @description Ehcache缓存工具类
 * @author zxkxc
 * @date 2023年02月06日
 */
@Slf4j
@Component
public class EhcacheUtil {

    public final CacheManager cacheManager;

    public EhcacheUtil(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }


    /**
     * 获取Cache类
     * @param cacheName 缓存名称
     * @return 缓存类
     */
    public Cache getCache(String cacheName) {
        return cacheManager.getCache(cacheName);
    }

    /**
     * 创建Cache类
     * @param cacheName 缓存名称
     * @return 缓存类
     */
    public Cache createCache(String cacheName) {
        if (!cacheManager.cacheExists(cacheName)) {
            Cache cache = new Cache(cacheName, 10000, false, true, Integer.MAX_VALUE, Integer.MAX_VALUE);
            cacheManager.addCache(cache);
        }
        return cacheManager.getCache(cacheName);
    }

    /**
     * 添加缓存数据
     * @param cacheName 缓存名称
     * @param key 缓存键值
     * @param value 缓存数据
     */
    public void put(String cacheName, String key, Object value) {
        try {
            Cache cache = cacheManager.getCache(cacheName);
            Element element = new Element(key, value);
            cache.put(element);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("添加缓存失败:{}",e.getMessage());
        }
    }

    /**
     * 添加缓存数据
     * @param cacheName 缓存名称
     * @param key 缓存键值
     * @param value 缓存数据
     * @param eternal 是否永久有效
     * @param time 有效时长(单位:秒)
     */
    public void put(String cacheName, String key, Object value, boolean eternal, int time) {
        try {
            Cache cache = cacheManager.getCache(cacheName);
            Element element = new Element(key, value);
            element.setEternal(eternal);
            element.setTimeToLive(time);
            cache.put(element);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("添加缓存失败:{}",e.getMessage());
        }
    }

    /**
     * 获取缓存数据
     * @param cacheName 缓存名称
     * @param key 缓存键值
     * @return 缓存数据
     */
    public Object get(String cacheName, String key) {
        try {
            Cache cache = cacheManager.getCache(cacheName);
            Element element = cache.get(key);
            return element == null ? null : element.getObjectValue();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取缓存数据失败:{}",e.getMessage());
            return null;
        }
    }

    /**
     * 删除缓存数据
     * @param cacheName 缓存名称
     * @param key 缓存键值
     */
    public void delete(String cacheName, String key) {
        try {
            Cache cache = cacheManager.getCache(cacheName);
            cache.remove(key);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("删除缓存数据失败:{}",e.getMessage());
        }
    }
}

3、使用Redis缓存

​ 虽然EhCache已经能够适用很多应用场景,但是由于EhCache是进程内的缓存框架,在集群模式下时,各应用服务器之间的缓存都是独立的,因此在不同服务器的进程间会存在缓存不一致的情况。即使EhCache提供了集群环境下的缓存同步策略,但是同步依然是需要一定的时间,短暂的缓存不一致依然存在。

​ 在一些要求高一致性(任何数据变化都能及时的被查询到)的系统和应用中,就不能再使用EhCache来解决了,这个时候使用集中式缓存就可以很好的解决缓存数据的一致性问题。接下来看看如何在Spring Boot的缓存支持中使用Redis实现数据缓存。

3-1、快速集成

第一步pom.xml中增加相关依赖:

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

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

第二步:配置文件中增加配置信息,以本地运行为例,比如:

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.shutdown-timeout=100ms

关于连接池的配置,注意几点:

  1. Redis的连接池配置在1.x版本中前缀为spring.redis.pool与Spring Boot 2.x有所不同。
  2. 在1.x版本中采用jedis作为连接池,而在2.x版本中采用了lettuce作为连接池
  3. 以上配置均为默认值,实际上生产需进一步根据部署情况与业务要求做适当修改.

第三步:执行请求,可以在控制台中输出了下面的内容

2021-12-08 10:32:23.483 TRACE 44087 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.data.redis.cache.RedisCacheManager
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where (user0_.name=? or ''=?) and (user0_.age=? or ? is null) order by user0_.id

2021-12-08 10:32:25.535 TRACE 44087 --- [nio-8888-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.cloud.controller.UserController#getUserList(User)
CacheManager type : class org.springframework.data.redis.cache.RedisCacheManager

使用redis缓存需要预先在当前本机/服务器上安装redis服务,以下大概描述下实操中在Mac中安装运行redis服务

1、访问redis.io/download网址下…

2、解压redis-6.2.6.tar.gz,拷贝到任意目录,例如/Users/houzx/

3、执行解压命令:tar zxcf redis-6.2.6.tar.gz

4、进入/Users/houzx/redis-6.2.6目录下,执行命令make && make install安装完成,配置都采用默认配置

5、执行src/redis-server命令启动服务,需要在redis解压目录下执行,如上述/Users/houzx/redis-6.2.6

可以看到:

  1. 第一行输出的CacheManager type为org.springframework.data.redis.cache.RedisCacheManager,而不是上一篇中的EhCacheCacheManager
  2. 第二次查询的时候,没有输出SQL语句,所以是走的缓存获取

3-2、Redis配置类

/**
 * @title RedisConfig
 * @description Redis配置类
 * @author zxkxc
 * @date 2022年03月06日
 */
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    private static final int DEFAULT_EXPIRE_TIME = 60 * 60 * 24;

    private static final String PARAM_CACHE_NAME = "paramsCache";

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(mapper);

        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofSeconds(DEFAULT_EXPIRE_TIME))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .disableCachingNullValues();

        // 配置自定义的缓存空间
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add(PARAM_CACHE_NAME);

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        //configMap.put(PARAM_CACHE_NAME, config.entryTtl(Duration.ofSeconds(DEFAULT_EXPIRE_TIME * 12)));

        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .initialCacheNames(cacheNames)
            .withInitialCacheConfigurations(configMap)
            .build();
    }
}

3-3、RedisUtil工具类

package com.zxkxc.cloud.common.utils.cache;

import com.zxkxc.cloud.common.utils.ReflectUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * @title RedisUtil
 * @description Redis缓存工具类
 * @author houzx
 * @date 2022年03月05日
 */
@Component
public class RedisUtil {

    public final RedisTemplate redisTemplate;

    public RedisUtil(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 设置有效时间
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return timeout > 0 && expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取缓存数据过期时间
     * @param key Redis键
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断是否存在缓存数据
     * @param key Redis键
     * @return true:存在;false:不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存数据
     * @param key Redis键
     * @return 删除结果
     */
    public boolean deleteKey(String... key) {
        if (key != null) {
            int length = key.length;
            if (length > 0) {
                if (length == 1) {
                    return redisTemplate.delete(key[0]);
                } else {
                    return redisTemplate.delete(CollectionUtils.arrayToList(key)) == length;
                }
            }
        }
        return false;
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  超时时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }


    /**
     * 获得缓存的基本对象。
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     * @param key 缓存键值
     * @return 删除结果
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     * @param collection 多个对象
     * @return 删除数量
     */
    public long deleteObject(final Collection collection) {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        for (T t : dataSet) {
            setOperation.add(t);
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     * @param key 缓存的键值
     * @param dataMap 缓存的数据
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获得缓存的集合
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheObjList(final String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 往Hash中存入数据
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 删除map中的数据
     * @param key   Redis键
     * @param hKeys ash键
     */
    public long deleteCacheMapValue(final String key, final Object...hKeys) {
        return redisTemplate.opsForHash().delete(key, hKeys);
    }

    /**
     * 获取Hash中的数据
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 获得缓存的基本对象列表
     * @param key 字符串前缀
     * @return 对象列表
     */
    public Collection<String> getCacheMapKeys(final String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取多个Hash中的数据
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 获取Hash中的数据
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey, Callable<T> valueLoader) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        Object cacheObj = opsForHash.get(key, hKey);
        Object value = null;
        if (cacheObj == null) {
            T call = getCallValue(valueLoader);
            if(null != call){
                value = call;
                redisTemplate.opsForHash().put(key, hKey, value);
                return(T) value;
            }else{
                return null;
            }
        } else {
            return (T) cacheObj;
        }
    }

    /**
     * 动态方法执行
     * @param valueLoader
     * @param <T>
     * @return
     */
    public <T> T getCallValue(Callable<T> valueLoader) {
        try {
            T call = valueLoader.call();
            if (ObjectUtils.isNotEmpty(call)) {
                Field field = ReflectUtil.getField(call.getClass(), "id");
                if (ObjectUtils.isNotEmpty(field) && ObjectUtils.isEmpty(ClassUtils.getMethod(call.getClass(), "id").invoke(call))) {
                    return null;
                }
                return call;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
        return null;
    }
}

写在最后;

小伙伴们有什么好的方法可以同时集成Ehcache、Redis相关依赖、配置、工具,但是可以在全局配置文件中根据需要仅启用某一个缓存呢??欢迎留言~