@javazjm
2017-11-01T20:45:23.000000Z
字数 11885
阅读 2639
Springboot
Cache
ehcache
redis
Springboot 使用缓存很简单,只需遵循以下几步就可以。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在启动类上加注解@enableCaching,启用缓存
在需要缓存的方法上,使用注解
1.@Cacheable(cacheNames = "car", key = "#name")
将方法的返回值 保存 在缓存“car”中,键由key指定,值是方法的返回值
2.@CachePut(cacheNames = "car", key = "#car.name")
使用方法的返回值 更新 缓存“car”中,键为key的值
3.@CacheEvict(cacheNames = "car", allEntries = true)
根据key和condition删除缓存,如果指定allEntries为true,则删除缓存中所有的对象
说明:
这里并没有明确指定缓存提供者,但是Springboot会按照以下顺序寻找提供者:
31.1.1. Generic
31.1.2. JCache (JSR-107)
31.1.3. EhCache 2.x
31.1.4. Hazelcast
31.1.5. Infinispan
31.1.6. Couchbase
31.1.7. Redis
31.1.8. Caffeine
31.1.9. Simple若没有查找到前9类缓存的cacheManager,则会使用最后一个simple缓存,也就是在内存中使用ConcurrentHashMap实现缓存。spring官方建议生产环境中勿使用simple缓存。
本实例就是使用的simple缓存。
注意:
1.
使用RedisTemplate,结果发现redis里查找不到对应的key-value键值对,原因是spring-data-redis的RedisTemplate
V>模板类在操作redis时默认使用JdkSerializationRedisSerializer来进行序列化.
2.
SpringBoot提供了对Redis的自动配置功能,在RedisAutoConfiguration中默认为我们配置了JedisConnectionFactory(客户端连接)、RedisTemplate以及StringRedisTemplate(数据操作模板),其中StringRedisTemplate模板只针对键值对都是字符型的数据进行操作
Spring会找到redisCacheManager,使用redis作为缓存。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
@Entity
@Table(name = "userInfo")
public class User implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String userName;
@Column(nullable = false)
private String passWord;
@Column(nullable = false)
private String email;
@Column(nullable = false, unique = true)
private String nickName;
@Column(nullable = false)
private Date regTime;
// setter getter...
}
/**
* @Cacheable 应用到读取数据的方法上,先从缓存中读取,如果没有再从DB获取数据,然后把数据添加到缓存中
* @param id
* keyGenerator 和 key 不能同时存在
* 否则:Both 'key' and 'keyGenerator' attributes have been set
* @return
*/
@Cacheable(value = "user", key = "'user'+ '_'+ #id", unless = "#result eq null")
public User getUserById(Long id) {
return entityManager.find(User.class, id);
}
@Cacheable(value = "user", key = "#root.methodName + '_users'", unless = "#result eq null ")
public List<User> getUsers(){
return userRepository.findAll();
}
/**
* 使用自定义注解
* @param user
* @return
*/
@UserSaveCache
public User addUser3(User user) {
return userRepository.save(user);
}
自定义注解
@Caching(put = {
@CachePut(value = "user", key = "'user' + #result.id"),
@CachePut(value = "user", key = "'user' + #result.nickName"),
@CachePut(value = "user", key = "'user' + #result.email"),
})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* key 生成策略
* @return
*/
@Bean
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 如果不做额外配置可以不需要,springboot会根据所使用的缓存,自动配置缓存管理器
* @param redisTemplate
* @return
*/
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
long expireTime = 600;
rcm.setDefaultExpiration(expireTime);//秒 管用
// System.out.println("通用过期时间:" + expireTime);
return rcm;
}
/**
* 连接工厂 默认自动配置JedisConnectionFactory(客户端连接)
* @return
*/
@Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, String> template = new RedisTemplate<>();
RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
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.setConnectionFactory(factory);
// 开启事务支持
template.setEnableTransactionSupport(true);
// template.setEnableDefaultSerializer(false); // 将任何序列化器设置为null,并使用原始字节数组来使用RedisTemplate
//key序列化方式 stringRedisSerializer
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
//value序列化 jackson2JsonRedisSerializer
template.setValueSerializer(jackson2JsonRedisSerializer);
// template.setValueSerializer(new RedisObjectSerializer()); // value是byte字节 \xAC\xED\x00\x05t\x00\x09\xE5\xBC\xA0\xE4\xBA\x8C\xE7\xA3\x8A
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
@Autowired
private StringRedisTemplate stringRedisTemplate; // 只能操作字符串
@Autowired
private RedisTemplate redisTemplate; // 通用性广
@Autowired
private UserService userService;
@Test
public void testSer(){
User user = new User("张无忌", "123456", "itzjm@qq.com", "zhangwuji", new Date());
User user1 = userService.addUser3(user);
System.out.println(user1.getId()+"=======");// 16
User userById = userService.getUserById(1L);
System.out.println(userById);
}
可以将redis的操作封装到一个类中RedisCacheService,直接在所需要的类中引入即可。
代码详见:https://github.com/JavaerZJM/springboot-workspace
参考:
根据官网:http://www.ehcache.org/blog/2016/05/18/ehcache3_jsr107_spring.html
<!-- ehcache 3.x -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.3.1</version>
</dependency>
<!-- JSR107 API -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<!-- springboot cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
需要在application.properties文件中配置:包括类路径和ehcache。xml文件
# ehcache配置
spring.cache.jcache.config=classpath:ehcache.xml
@Cacheable(value = "studentCache", key = "'student_'+#id")
@Override
public Student findById(Long id) {
return studentDao.findOne(id);
}
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>
<service>
<jsr107:defaults>
<!-- 定义别名为 "studentCache"的缓存,继承自缓存模板“heap - cache” -->
<jsr107:cache name="studentCache" template="heap-cache"/>
</jsr107:defaults>
</service>
<cache-template name="heap-cache">
<!-- 添加缓存事件监听器,当以下事件发生时,被EventLogger记录 -->
<listeners>
<listener>
<class>com.jimzhang.listener.EventLoggerListener</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on>
<events-to-fire-on>UPDATED</events-to-fire-on>
<events-to-fire-on>EXPIRED</events-to-fire-on>
<events-to-fire-on>REMOVED</events-to-fire-on>
<events-to-fire-on>EVICTED</events-to-fire-on>
</listener>
</listeners>
<resources>
<!-- 堆设置保存2000个条目 -->
<heap unit="entries">2000</heap>
<!-- 存储空间100M -->
<offheap unit="MB">100</offheap>
</resources>
</cache-template>
</config>
@Component
// 开启缓存
@EnableCaching
public class JSRCacheConfiguration implements JCacheManagerCustomizer {
@Override
public void customize(CacheManager cacheManager) {
cacheManager.createCache("studentCache", new MutableConfiguration<>()
.setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 10)))
.setStoreByValue(false)
.setStatisticsEnabled(true));
}
}
EventLoggerListener
public class EventLoggerListener implements CacheEventListener<Object, Object> {
private static final Logger LOGGER = LoggerFactory.getLogger(EventLoggerListener.class);
@Override
public void onEvent(CacheEvent<?, ?> event) {
LOGGER.info("Event: " + event.getType() + " Key: " + event.getKey() + " old value: " + event.getOldValue()
+ " new value: " + event.getNewValue());
}
}
单元测试:
@Test
public void test() {
Student student = studentService.findById(1L);
logger.info("学生信息----:" + student.toString());
Student student2 = studentService.findById(1L);
logger.info("学生信息2----:" + student2.toString());
}
<!-- Spring 对Cache 的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--EHCache 相关依赖 2.x-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
创建ehcache.xml文件
<?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">
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" />
<cache
name="studentCache"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU" />
配置文件类
@SpringBootConfiguration
/** 启用缓存 **/
@EnableCaching
public class EHCacheConfiguration {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* ehcache 主要的管理器
*
* @param bean
* @return
*/
@Bean
public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean) {
logger.info("CacheConfiguration.ehCacheCacheManager()");
// return new EhCacheCacheManager(bean.getObject());
return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
}
/**
* 默认情况下,其实可以不配置,EhCacheCacheConfiguration 类中默认使用EHCache 就是使用的classpath:ehcache.xml
*
* @return
*/
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
logger.info("CacheConfiguration.ehCacheManagerFactoryBean()");
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("config/ehcache.xml"));
//也说是说通过这个来设置cache的基地是这里的Spring独用,还是跟别的(如hibernate的Ehcache共享)
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
}
在application.properties配置文件制定ehcache.xml的位置
spring.cache.ehcache.config=classpath:config/another-config.xml
其实,可以不指定,Springboot只要发现了该配置文件,就会创建EhCache的缓存管理器。
代替ehcache.xml配置文件
@SpringBootConfiguration
@EnableCaching
public class CacheConfig {
@Bean
CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheManager());
}
@Bean(destroyMethod = "shutdown")
net.sf.ehcache.CacheManager ehCacheManager() {
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("studentCache");
cacheConfiguration.setTimeToLiveSeconds(600); // 保存10分钟
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
return new net.sf.ehcache.CacheManager(config);
}
}
@Cacheable(value = "studentCache", key = "'student_'+#id")
@Override
public Student findById(Long id) {
return studentDao.findOne(id);
}
@Test
public void test() {
Student student = studentService.findById(1L);
logger.info("学生信息----:" + student.toString());
Student student2 = studentService.findById(1L);
logger.info("学生信息2----:" + student2.toString());
}
参考: