缓存是应对高并发查询的利器,传统的spring使用缓存配置稍显笨重,springboot与缓存的结合使用,往往只需要添加依赖增加一行注解就能满足我们的基本使用。常用的缓存有ehcache,guava cache,memcached,redis以及caffeine等等。此篇我们结合springboot分析一下ehcache,caffeine和redis缓存的使用方式。
springboot&ehcache
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。Ehcache是一种广泛使用的开 源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
1.添加ehcache依赖
推荐使用的是ehcache2.x:
<!--开启 cache 缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache 缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2.添加ehcache配置文件
在src/resources目录添加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" />
<!-- 这里的 users 缓存空间是为了下面的 demo 做准备 -->
<cache
name="users"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
3.启用缓存
在应用启动类上增加启用缓存注解:
@EnableCaching
4.编写测试代码
缓存一般加在service层:
@Service
@Slf4j
public class UserService {
private static final String DEMO_CACHE_NAME = "users";
@Autowired
private UserDao userDao;
@Cacheable(value = DEMO_CACHE_NAME,key = "'user_' + #id")
public List<User> findById(Long id) {
log.info("UserService.findById query from DB;id={}",id);
return this.userDao.findById(id);
}
}
请求入口controller:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public Object queryUser(@PathVariable("id") Long id) {
return this.userService.findById(id);
}
}
5.运行&测试
运行应用启动类,浏览器输入http://localhost:8080/user/1:
观察应用访问日志:
第一次查询查询了DB。再次刷新页面发送请求:
没有打印访问DB的日志,也就是第一次请求之后把数据放入的缓存,第二次访问的时候直接从缓存取数据,缓存生效。
springboot&caffeine
Caffeine是使用Java8对Guava缓存的重写版本,在Spring Boot 2.0中将取代Guava。如果出现Caffeine,CaffeineCacheManager将会自动配置。springboot2.0.5依赖的基础库是spring5.x,而spring5中已经取消了对guava缓存的支持。
1.添加caffeine依赖
<!--开启 cache 缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
2.增加属性配置
在主配置属性文件application.properties中增加:
spring.cache.cache-names=users
spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=100s,refreshAfterWrite=5s
使用spring.cache.cache-names属性可以在启动时创建缓存,多个之间用逗号隔开。
3.增加caffeine配置类
CaffeineConfiguration:
@Configuration
public class CaffeineConfiguration {
/**
* 必须要指定这个Bean,refreshAfterWrite=5s这个配置属性才生效
* @return
*/
@Bean
public CacheLoader<Object, Object> cacheLoader() {
CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() {
@Override
public Object load(Object key) throws Exception {
return null;
}
// 重写这个方法将oldValue值返回回去,进而刷新缓存
@Override
public Object reload(Object key, Object oldValue) throws Exception {
return oldValue;
}
};
return cacheLoader;
}
}
4.运行&测试
业务代码service层和controller等不用调整。运行应用启动类,浏览器输入http://localhost:8080/user/1:
观察应用访问日志:
第一次查询查询了DB。再次刷新页面发送请求:
没有打印访问DB的日志,访问的时候直接从缓存取数据,也就说明我们配置的caffeine缓存生效。
springboot&redis
前边两种方式都是进程缓存,对于分布式架构盛行的时代,分布式缓存redis大放异彩。
1.添加redis缓存依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
2.增加属性配置
在src/resources/application.properties中增加redis连接配置:
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=119.23.232.185
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=bhx5211314
# 连接池最大连接数(使用负值表示没有限制)
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=1000
3.增加redis缓存配置类
使用FastJson代替默认的序列化方式:
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (null == t) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (null == bytes || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
配置自定义序列化方式:
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfiguration extends RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//使用fastjson序列化
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
4.运行&测试
业务代码不用修改。运行应用启动类,在发送请求之前查看redis是否存储的内容:
没有我们定义的users开头的缓存,浏览器输入http://localhost:8080/user/1:
观察应用访问日志:
第一次查询查询了DB。再次刷新页面发送请求:
没有打印访问DB的日志,访问的时候直接从缓存取数据,也就说明我们配置的缓存生效。这时我们再次查看redis存储的内容:
我们从DB查询的数据已经存储到了redis中。
总结
合理的使用缓存,能够提高应用的性能和吞吐能力,但是缓存的滥用也会带来分布式环境下数据不一致问题,具体springboot编程时,选择使用哪一种缓存方式,还需要根据具体业务场景判定。还有其他几种常用的缓存此处不在一一列举其使用方式,如果感兴趣可以自己实现一下memcached缓存等。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!