springboot整合缓存cache详解
缓存
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间
缓存使用
导入缓存技术对应的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
启用缓存
@SpringBootApplication
//开启缓存的注解
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
设置当前操作的结果数据进入缓存
//value表示缓存空间名称,key表示存入的关键字,查询是根据key来查询
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
SpringBoot提供的缓存技术除了提供默认的缓存方案,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发与管理
- Generic
- JCache
Ehcache
- Hazelcast
- Infinispan
- Couchbase
Redis
- Caffeine
- Simple(默认)
memcached
需求
输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)
输入手机号和验证码验证结果
需求分析
提供controller,传入手机号,业务层通过手机号计算出独有的6位验证码数据,存入缓存后返回此数据
提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入验证码进行比对,返回比对结果
解决上诉需求
1.开启缓存
@SpringBootApplicatio
//下面注解开启缓存
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
2.业务层接口
public interface SMSCodeService {
/**
* 传入手机号获取验证码,存入缓存
* @param tele
* @return
* */
String sendCodeToSMS(String tele);
/**
* 传入手机号与验证码,校验匹配是否成功
* @param smsCode
* @return
* */
boolean checkCode(SMSCode smsCode);
}
3.业务层设置获取验证码操作,并存储缓存,手机号为key,验证码为value
@Autowired
private CodeUtils codeUtils;
@CachePut(value = "smsCode",key="#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
CodeUtils
@Component
public class CodeUtils {
private String [] patch = {"000000","00000","0000","000","00","0",""};
public String generator(String tele){
int hash = tele.hashCode();
int encryption = 20206666;
long result = hash ^ encryption;
long nowTime = System.currentTimeMillis();
result = result ^ nowTime;
long code = result % 1000000;
code = code < 0 ? -code : code;
String codeStr = code + "";
int len = codeStr.length();
return patch[len] + codeStr;
}
@Cacheable(value = "smsCode",key="#tele")
public String get(String tele){
return null;
}
业务层设置校验验证码操作,校验码通过缓存读取,返回校验结果
@Autowired
private CodeUtils codeUtils;
public boolean checkCode(SMSCode smsCode) {
//取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
String code = smsCode.getCode();
String cacheCode = codeUtils.get(smsCode.getTele());
return code.equals(cacheCode);
}
缓存供应商变更:Ehcache
加入Ehcache坐标(缓存供应商实现)
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
缓存设定为使用Ehcache
spring:
cache:
type: ehcache
ehcache:
config: ehcache.xml
提供ehcache配置文件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">
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
提供ehcache配置文件ehcache.xml
<cache
name="smsCode"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
缓存供应商变更:Redis
加入Redis坐标(缓存供应商实现)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置Redis服务器,缓存设定为使用Redis
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
use-key-prefix: true # 是否使用前缀名(系统定义前缀名)
key-prefix: sms_ # 追加自定义前缀名
time-to-live: 10s # 有效时长
cache-null-values: false # 是否允许存储空值
缓存供应商变更:memcached
下载memcached
地址:https://www.runoob.com/memcached/window-install-memcached.html
安装memcached
使用管理员身份运行cmd指令
安装输入下面命令,并配置到服务当中
memcached.exe -d install
运行memcached
启动服务
memcached.exe -d start
停止服务
memcached.exe -d stop
memcached客户端选择
Memcached Client for Java:最早期客户端,稳定可靠,用户群广
SpyMemcached:效率更高
Xmemcached:并发处理更好
SpringBoot未提供对memcached的整合,需要使用硬编码方式实现客户端初始化管理
加入Xmemcache坐标(缓存供应商实现)
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
配置memcached服务器必要属性
memcached:
# memcached服务器地址
servers: localhost:11211
# 连接池的数量
poolSize: 10
# 设置默认操作超时
opTimeout: 3000
创建读取属性配置信息类,加载配置
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private Integer poolSize;
private Long opTimeout;
}
创建客户端配置类
@Configuration
public class XMemcachedConfig {
@Autowired
private XMemcachedProperties xMemcachedProperties;
@Bean
public MemcachedClient getMemcachedClinet() throws IOException {
MemcachedClientBuilder builder = new XMemcachedClientBuilder(xMemcachedProperties.getServers());
MemcachedClient memcachedClient = builder.build();
return memcachedClient;
}
}
配置memcached属性
@Service
public class SMSCodeServiceMemcacheImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private MemcachedClient memcachedClient;
@Override
public String sendCodeToSMS(String tele) {
String code = this.codeUtils.generator(tele);
//将数据加入memcache
try {
memcachedClient.set(tele,0,code);
// key,timeout,value
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
@Override
public boolean checkCode(CodeMsg codeMsg) {
String value = null;
try {
value = memcachedClient.get(codeMsg.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return codeMsg.getCode().equals(value);
}
}
缓存供应商变更:jetcache
jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能
jetCache设定了本地缓存与远程缓存的多级缓存解决方案
- 本地缓存(local)
- LinkedHashMap
- Caffeine
- 远程缓存(remote)
- Redis
- Tair
加入jetcache坐标
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
配置远程缓存必要属性
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
配置本地缓存必要属性
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
配置范例
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
limit: 100
remote:
default:
host: localhost
port: 6379
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
配置属性说明
开启jetcache注解支持
@SpringBootApplication
@EnableCreateCacheAnnotation
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
声明缓存对象
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(name = "smsCache", expire = 3600)
private Cache<String, String> jetSMSCache;
}
操作缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Override
public String sendCodeToSMS(String tele) {
String code = this.codeUtils.generator(tele);
jetSMSCache.put(tele,code);
return code;
}
@Override
public boolean checkCode(CodeMsg codeMsg) {
String value = jetSMSCache.get(codeMsg.getTele());
return codeMsg.getCode().equals(value);
}
}
启用方法注解
在启动类上加
@EnableMethodCache(basePackages = "com.itheima")
使用方法注解操作缓存
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Cached(name = "smsCache_", key = "#id", expire = 3600)
@CacheRefresh(refresh = 10,timeUnit = TimeUnit.SECONDS)
public Book getById(Integer id) {
return bookDao.selectById(id);
}
@CacheUpdate(name = "smsCache_", key = "#book.id", value = "#book")
public boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
@CacheInvalidate(name = "smsCache_", key = "#id")
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
}
缓存对象必须保障可序列化(实现Serializable)
@Data
public class Book implements Serializable {
}
查看缓存统计报告
jetcache:
statIntervalMinutes: 15
缓存供应商变更:j2cache
j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身不提供缓存功能
基于 ehcache + redis 进行整合
加入j2cache坐标,加入整合缓存的坐标
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
配置使用j2cache(application.yml)
j2cache:
config-location: j2cache.properties
配置一级缓存与二级缓存以及一级缓存数据到二级缓存的发送方式(j2cache.properties)
# 配置1级缓存
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml
# 配置1级缓存数据到2级缓存的广播方式:可以使用redis提供的消息订阅模式,也可以使用jgroups多播实现
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
# 配置2级缓存
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379
设置使用缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private CacheChannel cacheChannel;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
cacheChannel.set("sms",tele,code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = cacheChannel.get("sms",smsCode.getTele()).asString();
return smsCode.getCode().equals(code);
}
}