目標(biāo)是
- 同時(shí)使用redis 和encache
- 部分緩存使用redis,部分緩存使用encache,可代碼自動選擇
在pom.xml中增加支持
<!-- 本地緩存依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>-->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<!-- redis緩存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.collections/google-collections -->
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0-rc5</version>
</dependency>
配置Application類
//通過exclude不注入數(shù)據(jù)源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DemocacheApplication {
public static void main(String[] args) {
SpringApplication.run(DemocacheApplication.class, args);
}
}
如果項(xiàng)目中沒有數(shù)據(jù)源可以使用如下注解
排除不進(jìn)行數(shù)據(jù)源配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
也可以在有yml配置中增加數(shù)據(jù)源 : 如數(shù)據(jù)庫等
CacheManagerConfig
package com.demo.cache;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
public class CacheManagerConfig {
private final CacheProperties cacheProperties;
CacheManagerConfig(CacheProperties cacheProperties) {
this.cacheProperties = cacheProperties;
}
/**
* cacheManager名字
*/
public interface CacheManagerNames {
/**
* redis
*/
String REDIS_CACHE_MANAGER = "redisCacheManager";
/**
* ehCache
*/
String EHCACHE_CACHE_MAANGER = "ehCacheCacheManager";
}
/**
* 緩存名,名稱暗示了緩存時(shí)長 注意: 如果添加了新的緩存名,需要同時(shí)在下面的RedisCacheCustomizer#RedisCacheCustomizer里配置名稱對應(yīng)的緩存時(shí)長
* ,時(shí)長為0代表永不過期;緩存名最好公司內(nèi)部唯一,因?yàn)榭赡芏鄠€(gè)項(xiàng)目共用一個(gè)redis。
*
* @see RedisCacheCustomizer#customize(RedisCacheManager)
*/
public interface CacheNames {
/** 15分鐘緩存組 */
String CACHE_15MINS = "cp_salary:cache:15m";
/** 30分鐘緩存組 */
String CACHE_30MINS = "cp_salary:cache:30m";
/** 60分鐘緩存組 */
String CACHE_60MINS = "cp_salary:cache:60m";
/** 180分鐘緩存組 */
String CACHE_180MINS = "cp_salary:cache:180m";
}
/**
* ehcache緩存名
*/
public interface EhCacheNames {
String CACHE_10MINS = "cp_salary:cache:10m";
String CACHE_20MINS = "cp_salary:cache:20m";
String CACHE_30MINS = "cp_salary:cache:30m";
}
/**
* 默認(rèn)的redisCacheManager
* @param redisTemplate 通過參數(shù)注入,這里沒有手動給它做配置。在引入了redis的jar包,并且往
* application.yml里添加了spring.redis的配置項(xiàng),springboot的autoconfig會自動生成一個(gè)
* redisTemplate的bean
* @return
*/
@Primary
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheManager cacheManager = RedisCacheManager.create(factory);
return cacheManager;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
StringRedisTemplate template = new StringRedisTemplate(factory);
setSerializer(template);//設(shè)置序列化工具
template.afterPropertiesSet();
return template;
}
/** cache的一些自定義配置 */
@Bean
public RedisCacheCustomizer redisCacheManagerCustomizer() {
return new RedisCacheCustomizer();
}
private static class RedisCacheCustomizer
implements CacheManagerCustomizer<RedisCacheManager> {
/** CacheManager緩存自定義初始化比較早,盡量不要@autowired 其他spring 組件 */
@Override
public void customize(RedisCacheManager cacheManager) {
// 自定義緩存名對應(yīng)的過期時(shí)間
Map<String, Long> expires = ImmutableMap.<String, Long>builder()
.put(CacheNames.CACHE_15MINS, TimeUnit.MINUTES.toSeconds(15))
.put(CacheNames.CACHE_30MINS, TimeUnit.MINUTES.toSeconds(30))
.put(CacheNames.CACHE_60MINS, TimeUnit.MINUTES.toSeconds(60))
.put(CacheNames.CACHE_180MINS, TimeUnit.MINUTES.toSeconds(180)).build();
// spring cache是根據(jù)cache name查找緩存過期時(shí)長的,如果找不到,則使用默認(rèn)值
/*cacheManager.setDefaultExpiration(TimeUnit.MINUTES.toSeconds(30));
cacheManager.setExpires(expires);*/
}
}
/**
* 創(chuàng)建ehCacheCacheManager
*/
@Bean
public EhCacheCacheManager ehCacheCacheManager() {
Resource p = this.cacheProperties.getEhcache().getConfig();
Resource location = this.cacheProperties
.resolveConfigLocation(p);
return new EhCacheCacheManager(EhCacheManagerUtils.buildCacheManager(location));
}
private void setSerializer(StringRedisTemplate template){
@SuppressWarnings({ "rawtypes", "unchecked" })
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
CacheConfig.java
將出錯(cuò)信息 寫入日志
package com.demo.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
/**
* Spring cache的一些配置,建議組件相關(guān)配置都放在相應(yīng)的configuration類中
*
* @author
*/
@Configuration
@ConditionalOnBean(RedisCacheManager.class)
public class CacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisCacheManager redisCacheManager;
/**
* 重寫這個(gè)方法,目的是用以提供默認(rèn)的cacheManager
* @return
*/
@Override
public CacheManager cacheManager() {
return redisCacheManager;
}
/** 如果cache出錯(cuò), 我們會記錄在日志里,方便排查,比如反序列化異常 */
@Override
public CacheErrorHandler errorHandler() {
return new LoggingCacheErrorHandler();
}
/* non-public */ static class LoggingCacheErrorHandler extends SimpleCacheErrorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
logger.error(String.format("cacheName:%s,cacheKey:%s",
cache == null ? "unknown" : cache.getName(), key), exception);
super.handleCacheGetError(exception, cache, key);
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key,
Object value) {
logger.error(String.format("cacheName:%s,cacheKey:%s",
cache == null ? "unknown" : cache.getName(), key), exception);
super.handleCachePutError(exception, cache, key, value);
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
logger.error(String.format("cacheName:%s,cacheKey:%s",
cache == null ? "unknown" : cache.getName(), key), exception);
super.handleCacheEvictError(exception, cache, key);
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
logger.error(String.format("cacheName:%s", cache == null ? "unknown" : cache.getName()),
exception);
super.handleCacheClearError(exception, cache);
}
}
}
CacheDemoService.java
測試代碼
@Service
public class CacheDemoService {
@Cacheable(key = "'key'", cacheManager = CacheManagerConfig.CacheManagerNames.EHCACHE_CACHE_MAANGER, cacheNames = CacheManagerConfig.EhCacheNames.CACHE_10MINS)
public String demo(String key) {
return "abc" + key;
}
//@Cacheable(key = "'key'", cacheNames = CacheManagerConfig.CacheNames.CACHE_15MINS)
@Cacheable(key = "'key'",cacheManager = CacheManagerConfig.CacheManagerNames.REDIS_CACHE_MANAGER, cacheNames = CacheManagerConfig.CacheNames.CACHE_15MINS)
public String demo2(String key) {
return "abcdemo2" + key;
}
}
出錯(cuò)提示
1.'org.springframework.cache.interceptor.CacheExpressionRootObject' -maybe not public
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'test' cannot be found on object of type 'org.springframework.cache.interceptor.CacheExpressionRootObject' - maybe not public?
將
@Cacheable(value = Fields.SYS_CACHE,key = "key")
@Cacheable(value = Fields.SYS_CACHE,key = "'key'")
2.java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisCacheManager' defined in class path resource [com/mx/config/CacheManagerConfig.class]: Unsatisfied dependency expressed through method 'redisCacheManager' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConnectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory]: Factory method 'redisConnectionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig
解決方法:
缺少依賴包
pom.xml 中加入
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
代碼demo下
democache-ehcache-redis-springboot.rar
鏈接:https://pan.baidu.com/s/1xZqpQQOcVgkr0W_FrItHRw 密碼:x2jo
參考鏈接
我的博客即將同步至騰訊云+社區(qū),邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1rys0lnd117vx