目标是
- 同时使用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不注入数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DemocacheApplication {
public static void main(String[] args) {
SpringApplication.run(DemocacheApplication.class, args);
}
}
如果项目中没有数据源可以使用如下注解
排除不进行数据源配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
也可以在有yml配置中增加数据源 : 如数据库等
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";
}
/**
* 缓存名,名称暗示了缓存时长 注意: 如果添加了新的缓存名,需要同时在下面的RedisCacheCustomizer#RedisCacheCustomizer里配置名称对应的缓存时长
* ,时长为0代表永不过期;缓存名最好公司内部唯一,因为可能多个项目共用一个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";
}
/**
* 默认的redisCacheManager
* @param redisTemplate 通过参数注入,这里没有手动给它做配置。在引入了redis的jar包,并且往
* application.yml里添加了spring.redis的配置项,springboot的autoconfig会自动生成一个
* 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);//设置序列化工具
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) {
// 自定义缓存名对应的过期时间
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是根据cache name查找缓存过期时长的,如果找不到,则使用默认值
/*cacheManager.setDefaultExpiration(TimeUnit.MINUTES.toSeconds(30));
cacheManager.setExpires(expires);*/
}
}
/**
* 创建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
将出错信息 写入日志
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的一些配置,建议组件相关配置都放在相应的configuration类中
*
* @author
*/
@Configuration
@ConditionalOnBean(RedisCacheManager.class)
public class CacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisCacheManager redisCacheManager;
/**
* 重写这个方法,目的是用以提供默认的cacheManager
* @return
*/
@Override
public CacheManager cacheManager() {
return redisCacheManager;
}
/** 如果cache出错, 我们会记录在日志里,方便排查,比如反序列化异常 */
@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;
}
}
出错提示
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
参考链接
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1rys0lnd117vx
网友评论