问题描述:企业级应用主要作用是信息处理,当需要读取数据时,由于受限于数据库的访问效率,导致整体系统性能偏低。(就是说:应用程序直接与数据库打交道,访问效率低。
解决方案:为了能够提高数据库的访问效率,降低数据库的压力:
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
private HashMap<Integer,Book> cache = new HashMap<Integer,Book>();
@Override
public Book getById(Integer id) {
//如果当前缓存中没有本次要查询的数据,则进行查询,否则直接从缓存中获取数据返回
Book book = cache.get(id);
if(book == null){
book = bookDao.selectById(id);
cache.put(id,book);
}
return book;
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
@SpringBootApplication
//开启缓存功能
@EnableCaching
public class ApplicationCache {
public static void main(String[] args) {
SpringApplication.run(ApplicationCache.class, args);
}
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
// @Cacheable声明当前方法的返回值放入缓存中
// value属性描述缓存的存储位置,可以理解为是一个存储空间名,
// key属性描述了缓存中保存数据的名称,使用#id读取形参中的id值作为缓存名称
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
@SpringBootApplication
//开启缓存功能
@EnableCaching
public class ApplicationCache {
public static void main(String[] args) {
SpringApplication.run(ApplicationCache.class,args);
}
}
@Data
public class PhoneCode {
// 手机号码
private String tele;
// 验证码
private String code;
}
@Component
public class CodeUtils {
// code不够6位数时,用来补0的
private String [] patch = {"000000","00000","0000","000","00","0",""};
public String generator(String tele){
// 获取tele的 hash值
int hash = tele.hashCode();
// 设置加密码
int encryption = 20206666;
// 哈希异或加密码
long result = hash ^ encryption;
// 获取当前的时间
long nowTime = System.currentTimeMillis();
// 再次执行一下异或
result = result ^ nowTime;
// 取6位数
long code = result % 1000000;
code = code < 0 ? -code : code;
String codeStr = code + "";
int len = codeStr.length();
return patch[len] + codeStr;
}
// 把这个获取缓存中的code放在一个bean里:就会走spring容器管理的bean的操作
// 因为CodeUtils实体类使用了@Component注解,所以它是一个受spring管理的bean
// 又因为在业务层的实现类( CodeServiceImpl)里面,使用@Autowired自动配置CodeUtils,
// 所以在( CodeServiceImpl)中使用CodeUtiles调用它里面的方法get的过程中,
// CodeUtiles按照bean的形式加载,方法上面的注解生效,就能从缓存中得到数据了。
// 如果这个@Cacheable不运行,那么下面的get方法就是return null
//(作为ioc容器管理的对象,其中的注解会生效,但如果不使用spring管理的bean对象就直接调用,那注解不会生效
//
// 要想要这个注解运行的核心是:这个注解得被spring容器加载到,
// 这个@Cacheable注解的功能是在引导类添加的@EnableCaching(开启缓存功能)提供的
@Cacheable(value = "saveCode",key="#tele")
public String get(String tele){
return null;
}
}
对于校验验证码的功能应该放入(受spring管理的bean对象里面)中进行,比如这里的工具类,因为如果想获取code的值,spring容器加载注解@Cacheable,才能获取到缓存里面的验证码。(如果上面的get方法没有按照bean的形式加载方法,那get方法上面的@Cacheable(value = “saveCode”,key=“#tele”) 就不会生效)
为啥放在业务层实现类上不行呢,它有@Service代表它也受spring容器管理,因为get方法是在业务实现类上被调用的,是用this调用的,只是走普通方法的调用,没有经过spring容器,因此@Cacheable也没有启动,所以返回null(同一个bean内部调用方法不经过spring)
下面这个才是说的比较清晰的理解:
当一个方法调用同一个类中另一个有注解的方法时,注解是不会生效的。这是因为Spring在扫描bean时,会为带有@Async注解的方法生成一个代理类,代理类负责增加异步作用。但是,如果有注解的方法被同一个类中的其他方法调用,调用并没有通过代理类,而是直接通过原来的那个bean,所以注解就不会生效。
public interface CodeService {
// 根据传进来的电话号码,用相应的算法,得出Code作为返回值
public String sendCode(String tele);
// 校验code
public boolean checkCode(PhoneCode smsCode);
}
@Service
public class CodeServiceImpl implements CodeService {
@Autowired
private CodeUtils codeUtils;
// @Cacheable(value = "saveCode", key = "#tele")
@CachePut(value = "saveCode", key = "#tele")
public String sendCode(String tele) {
// 生成验证码
String code = codeUtils.generator(tele);
return code;
}
public boolean checkCode(PhoneCode pCode) {
//取出内存中的验证码 与 传递过来的验证码比对,如果相同,返回true
// 传过来的值
String code = pCode.getCode();
// 获取内存中的值(基于bean进行调用)
String cacheCode = codeUtils.get(pCode.getTele());
return code.equals(cacheCode);
}
}
获取验证码后,当验证码失效时必须重新获取验证码,因此在获取验证码的功能上不能使用@Cacheable注解,@Cacheable注解是缓存中没有值则放入值,缓存中有值则取值。此处的功能仅仅是生成验证码并放入缓存,并不具有从缓存中取值的功能,因此不能使用@Cacheable注解,应该使用仅具有向缓存中保存数据的功能,使用@CachePut注解即可。
@RestController
@RequestMapping("/code")
public class CodeController {
@Autowired
private CodeService codeService;
@GetMapping
public String getCode(String tele){
String code = codeService.sendCode(tele);
return code;
}
@PostMapping
public boolean checkCode(PhoneCode code){
return codeService.checkCode(code);
}
}
Ehcache是一种缓存技术,使用springboot整合Ehcache其实就是变更一下缓存技术的实现方式,代码都和上面的一样。
步骤①:导入Ehcache的坐标
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
说明:这里为什么不是导入Ehcache的starter,而是导入技术坐标呢?
其实springboot整合缓存技术做的是通用格式,不管你整合哪种缓存技术,只是实现变化了,操作方式是一样的。这也体现出springboot技术的优点,统一同类技术的整合方式。
spring:
cache:
type: ehcache #配置缓存的类型type为ehcache
ehcache:
config: classpath:ehcache.xml # 指定ehcache的配置文件,文件名如果使用ehcache.xml则不配置也可以(默认),更改了文件名才需要指定
配置缓存的类型type为ehcache,此处需要说明一下,当前springboot可以整合的缓存技术中包含有ehcach,所以可以这样书写。type的值必须是springboot可以整合的缓存技术中包含有的
由于ehcache的配置有独立的配置文件格式,因此还需要指定ehcache的配置文件,以便于读取相应配置
<?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">
<diskStore path="D:\ehcache" /> <!--存储位置 -->
<!--默认缓存策略 -->
<!-- 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" />
<!--@CachePut(value = "saveCode", key = "#tele"),这里配置了value值,就是指定使用哪个缓存,需要对该缓存进行配置
如果没有配置value属性,就使用上面的默认缓存
-->
<!-- name属性用来区分是哪一个缓存 -->
<cache
name="saveCode"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
按照这上面的步骤做完后,直接就可以运行上面的手机验证码案例(就是改变了配置和缓存供应商,代码都不需要改变)
注意上面的验证码设置的缓存,设置了数据保存的位置是saveCode
@CachePut(value = "saveCode", key = "#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
//这个设定需要保障ehcache中有一个缓存空间名称叫做smsCode的配置,前后要统一。
// 在企业开发过程中,通过设置不同名称的cache来设定不同的缓存策略,应用于不同的缓存数据。
总结:可以发现一点,原始代码没有任何修改,仅仅是加了一组配置就可以变更缓存供应商了。
步骤①:导入redis的坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
步骤②:配置缓存技术实现使用redis
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
如果需要对redis作为缓存进行配置,注意不是对原始的redis进行配置,而是配置redis作为缓存使用相关的配置,隶属于spring.cache.redis节点下。(还有就是运行的时候记得打开Redis服务器。
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
redis:
use-key-prefix: false
key-prefix: sms_
cache-null-values: false
time-to-live: 10s
步骤①:导入xmemcached的坐标
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
步骤②:配置memcached,制作memcached的配置类
对于服务器的配置如果使用硬编码,就会写死到了代码。对此,应该将此数据提取出来,做成独立的配置属性。(在制作配置类之前在配置文件自定义对应的数据配置)
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private int poolSize;
private long opTimeout;
}
memcached:
servers: localhost:11211
poolSize: 10
opTimeout: 3000
@Configuration
public class XMemcachedConfig {
@Autowired
private XMemcachedProperties props;
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(props.getServers());
memcachedClientBuilder.setConnectionPoolSize(props.getPoolSize());
memcachedClientBuilder.setOpTimeout(props.getOpTimeout());
MemcachedClient memcachedClient = memcachedClientBuilder.build();
return memcachedClient;
}
}
memcached默认对外服务端口11211。
步骤③:使用xmemcached客户端操作缓存,注入MemcachedClient对象
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private MemcachedClient memcachedClient;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
try {
memcachedClient.set(tele,10,code);
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = null;
try {
code = memcachedClient.get(smsCode.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return smsCode.getCode().equals(code);
}
}
默认使用的远程方案是redis
:<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
# 定义端口
server:
port: 80
# 缓存的配置
jetcache:
remote: # 选择本地(local)的或者远程(remote)的
default: # default是一个名称,可以自己设置,但是在创建缓存时指定参数area,声明使用对应缓存即可
type: redis # 选定缓存方案,这里默认就是redis
host: localhost # 地址
port: 6379 # 端口配置
keyConvertor: fastjson # 指定key转换为字符串的工具(对象需要转换为json字符串)
valueEncode: java # 这两个value是设定存储的值做转换的时候,转换成什么样的数据
valueDecoder: java
poolConfig: # 这个配置必须配置
maxTotal: 50 # 最大连接数
ssm: # 名为ssm的缓存
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecoder: java
poolConfig:
maxTotal: 50
其中poolConfig是必配项,否则会报错
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class jetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(jetcacheApplication.class,args);
}
}
@Service
public class CodeServiceImpl implements CodeService {
@Autowired
private CodeUtils codeUtils;
// @CreateCache(area="ssm",name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS)
@CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS)
// name属性表示缓存里面的key名的前缀
// expire属性是设置过期时间
// timeUnit属性设置过期时间的单位,默认就是秒
// area属性指定使用哪个缓存(这个缓存必须在配置文件里面有配置)
private Cache<String ,String> jetCache;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
// 放进缓存里面
jetCache.put(tele,code);
return code;
}
@Override
public boolean checkCode(Code smsCode) {
// 从缓存里面取出来
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
通过上述jetcache使用远程方案连接redis可以看出,jetcache操作缓存时的接口操作更符合开发者习惯,使用缓存就先获取缓存对象Cache,放数据进去就是put,取数据出来就是get,更加简单易懂。并且jetcache操作缓存时,可以为某个缓存对象设置过期时间,将同类型的数据放入缓存中,方便有效周期的管理。
jetcache:
local: # 把这里的local改成remote就是远程缓存了,但是下面的type属性需要更改,因为远程缓存和本地缓存使用的技术不一样
default:
type: linkedhashmap # 选用缓存方案
keyConvertor: fastjson
为了加速数据获取时key的匹配速度,jetcache要求指定key的类型转换器。简单说就是,如果你给了一个Object作为key的话,我先用key的类型转换器给转换成字符串,然后再保存。等到获取数据时,仍然是先使用给定的Object转换成字符串,然后根据字符串匹配。由于jetcache是阿里的技术,这里推荐key的类型转换器使用阿里的fastjson。
步骤③:启用缓存
步骤④:创建缓存对象Cache时,标注当前使用本地缓存
@Service
public class CodeServiceImpl implements CodeService {
@Autowired
private CodeUtils codeUtils;
// name属性表示缓存里面的key名的前缀
// expire属性是设置过期时间
// timeUnit属性设置过期时间的单位,默认就是秒
// area属性指定使用哪个缓存(这个缓存必须在配置文件里面有配置)
@CreateCache(area="ssm",name="jetCache_",expire = 100,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL) //cacheType指定使用哪个缓存,默认是远程
private Cache<String ,String> jetCache;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
// 放进缓存里面
jetCache.put(tele,code);
return code;
}
@Override
public boolean checkCode(Code smsCode) {
// 从缓存里面取出来
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
jetcache:
remote: # 选择本地(local)的或者远程(remote)的
default: # default是一个名称,可以自己设置,但是在创建缓存时指定参数area,声明使用对应缓存即可
type: redis # 选定缓存方案,这里默认就是redis
host: localhost # 地址
port: 6379 # 端口配置
keyConvertor: fastjson # 指定key转换为字符串的工具(对象需要转换为json字符串)
valueEncode: java # 这两个value是设定存储的值做转换的时候,转换成什么样的数据,就是说进入缓存,和从缓存出来的格式都保证一致(比如把对象序列化放进缓存里面,再从缓存里面把数据反序列化出来)
valueDecoder: java
poolConfig: # 这个配置必须配置
maxTotal: 50 # 最大连接数
local:
ssm: # 名为ssm的缓存
type: linkedhashmap
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecoder: java
poolConfig:
maxTotal: 50
在创建缓存的时候,配置cacheType为BOTH即则本地缓存与远程缓存同时使用。
@Service
public class CodeServiceImpl implements CodeService {
@CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.BOTH)
private Cache<String ,String> jetCache;
}
cacheType如果不进行配置,默认值是REMOTE,即仅使用远程缓存方案。关于jetcache的配置,参考以下信息
属性 | 默认值 | 说明 |
---|---|---|
jetcache.statIntervalMinutes | 0 | 统计间隔,0表示不统计 |
jetcache.hiddenPackages | 无 | 自动生成name时,隐藏指定的包名前缀 |
jetcache.[local|remote].${area}.type | 无 | 缓存类型,本地支持linkedhashmap、caffeine,远程支持redis、tair |
jetcache.[local|remote].${area}.keyConvertor | 无 | key转换器,当前仅支持fastjson |
jetcache.[local|remote].${area}.valueEncoder | java | 仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local|remote].${area}.valueDecoder | java | 仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local|remote].${area}.limit | 100 | 仅local类型的缓存需要指定,缓存实例最大元素数 |
jetcache.[local|remote].${area}.expireAfterWriteInMillis | 无穷大 | 默认过期时间,毫秒单位 |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 仅local类型的缓存有效,毫秒单位,最大不活动间隔 |
?以上方案仅支持手工控制缓存,但是springcache方案中的方法缓存特别好用,给一个方法添加一个注解,方法就会自动使用缓存。jetcache也提供了对应的功能,即方法缓存。
jetcache提供了方法缓存方案,只不过名称变更了而已。在对应的操作接口上方使用注解@Cached即可
步骤①:导入springboot整合jetcache对应的坐标starter
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
jetcache:
local: # 本地缓存的配置(可以配置多个,名称要不一样)
default: # 缓存的名称(自定义)
type: linkedhashmap
keyConvertor: fastjson
remote: # 远程缓存的配置(可以配置多个,名称要不一样)
default: # 缓存的名称(自定义)
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
由于redis缓存中不支持保存对象,因此需要对redis设置当Object类型数据进入到redis中时如何进行类型转换。需要配置keyConvertor表示key的类型转换方式,同时标注value的转换类型方式,值进入redis时是java类型,标注valueEncode为java,值从redis中读取时转换成java,标注valueDecode为java。
注意,为了实现Object类型的值进出redis,需要保障进出redis的Object类型的数据必须实现序列化接口:Serializable。
@Data
public class Book implements Serializable {
private Integer id;
private String type;
private String name;
private String description;
}
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
//开启方法注解缓存
@EnableMethodCache(basePackages = "com.knife")
public class Springboot20JetCacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetCacheApplication.class, args);
}
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
public Book getById(Integer id) {
return bookDao.selectById(id);
}
}
更新缓存
@CacheUpdate(name="book_",key="#book.id",value="#book")
public boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
删除缓存
@CacheInvalidate(name="book_",key = "#id")
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
定时刷新缓存
@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 5) //这里表示5秒刷新一次
public Book getById(Integer id) {
return bookDao.selectById(id);
}
jetcache还提供有简单的数据报表功能,帮助开发者快速查看缓存命中信息,只需要添加一个配置即可
jetcache:
statIntervalMinutes: 1 # 数据报表功能
local: # 本地缓存的配置(可以配置多个,名称要不一样)
default: # 缓存的名称(自定义)
type: linkedhashmap
keyConvertor: fastjson
remote: # 远程缓存的配置(可以配置多个,名称要不一样)
default: # 缓存的名称(自定义)
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
设置后,每1分钟在控制台输出缓存数据命中信息
[DefaultExecutor] c.alicp.jetcache.support.StatInfoLogger : jetcache stat from 2022-02-28 09:32:15,892 to 2022-02-28 09:33:00,003
cache | qps| rate| get| hit| fail| expire| avgLoadTime| maxLoadTime
---------+-------+-------+------+-------+-------+---------+--------------+--------------
book_ | 0.66| 75.86%| 29| 22| 0| 0| 28.0| 188
---------+-------+-------+------+-------+-------+---------+--------------+--------------
<!-- j2cache需要的依赖-->
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<!-- j2cache的starter中默认包含了redis坐标,官方推荐使用redis作为二级缓存,因此此处无需导入redis坐标-->
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<!-- ehcache的依赖-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
application.yml文件
server:
port: 80
j2cache:
config-location: j2cache.properties
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">
<diskStore path="D:\ehcache" />
<!--默认缓存策略 -->
<!-- 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>
j2ehcache.properties文件
# 1级缓存的配置
# j2cache的一级缓存供应商是ehcache
j2cache.L1.provider_class=ehcache
# ehcache的配置文件是ehcache.xml
ehcache.config.Xml= ehcache.xml
# 配置是否开启二级缓存,默认为true
j2cache.l2-cache-open = true
# 2 级缓存的配置
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379
# 选择redis的服务器,single表示使用单极模式的服务器
redis.mode = single
# key的命名前缀
redis.namespace = j2cache
# 1级缓存中的数据如何到达二级缓存
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
此处配置不能乱配置,需要参照官方给出的配置说明进行。例如1级供应商选择ehcache,供应商名称仅仅是一个ehcache,但是2级供应商选择redis时要写专用的Spring整合Redis的供应商类名SpringRedisProvider,而且这个名称并不是所有的redis包中能提供的,也不是spring包中提供的。因此配置j2cache必须参照官方文档配置,而且还要去找专用的整合包,导入对应坐标才可以使用。
一级与二级缓存最重要的一个配置就是两者之间的数据沟通方式,此类配置也不是随意配置的,并且不同的缓存解决方案提供的数据沟通方式差异化很大,需要查询官方文档进行设置。
步骤③:使用缓存
@Service
public class CodeServiceImpl implements CodeService {
@Autowired
private CodeUtils codeUtils;
//定义一个缓存的对象
@Autowired
private CacheChannel cacheChannel;
@Override
public String sendCode(String tele) {
String code = codeUtils.generator(tele);
// 把数据放进缓存里面
cacheChannel.set("sms",tele,code);
return code;
}
@Override
public boolean checkCode(Code smsCode) {
// 从缓存中获取数据
String code = cacheChannel.get("sms",smsCode.getTele()).asString();
return smsCode.getCode().equals(code);
}
}
j2cache的使用和jetcache比较类似,但是无需开启使用的开关,直接定义缓存对象即可使用,缓存对象名CacheChannel。
#J2Cache configuration
#########################################
# Cache Broadcast Method
# values:
# jgroups -> use jgroups's multicast
# redis -> use redis publish/subscribe mechanism (using jedis)
# lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend)
# rabbitmq -> use RabbitMQ publisher/consumer mechanism
# rocketmq -> use RocketMQ publisher/consumer mechanism
# none -> don't notify the other nodes in cluster
# xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy
#########################################
j2cache.broadcast = redis
# jgroups properties
jgroups.channel.name = j2cache
jgroups.configXml = /network.xml
# RabbitMQ properties
rabbitmq.exchange = j2cache
rabbitmq.host = localhost
rabbitmq.port = 5672
rabbitmq.username = guest
rabbitmq.password = guest
# RocketMQ properties
rocketmq.name = j2cache
rocketmq.topic = j2cache
# use ; to split multi hosts
rocketmq.hosts = 127.0.0.1:9876
#########################################
# Level 1&2 provider
# values:
# none -> disable this level cache
# ehcache -> use ehcache2 as level 1 cache
# ehcache3 -> use ehcache3 as level 1 cache
# caffeine -> use caffeine as level 1 cache(only in memory)
# redis -> use redis as level 2 cache (using jedis)
# lettuce -> use redis as level 2 cache (using lettuce)
# readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available.
# memcached -> use memcached as level 2 cache (xmemcached),
# [classname] -> use custom provider
#########################################
j2cache.L1.provider_class = caffeine
j2cache.L2.provider_class = redis
# When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations
# j2cache.L2.config_section = redis
# Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true)
# NOTICE: redis hash mode (redis.storage = hash) do not support this feature)
j2cache.sync_ttl_to_redis = true
# Whether to cache null objects by default (default false)
j2cache.default_cache_null_object = true
#########################################
# Cache Serialization Provider
# values:
# fst -> using fast-serialization (recommend)
# kryo -> using kryo serialization
# json -> using fst's json serialization (testing)
# fastjson -> using fastjson serialization (embed non-static class not support)
# java -> java standard
# fse -> using fse serialization
# [classname implements Serializer]
#########################################
j2cache.serialization = json
#json.map.person = net.oschina.j2cache.demo.Person
#########################################
# Ehcache configuration
#########################################
# ehcache.configXml = /ehcache.xml
# ehcache3.configXml = /ehcache3.xml
# ehcache3.defaultHeapSize = 1000
#########################################
# Caffeine configuration
# caffeine.region.[name] = size, xxxx[s|m|h|d]
#
#########################################
caffeine.properties = /caffeine.properties
#########################################
# Redis connection configuration
#########################################
#########################################
# Redis Cluster Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (\u6570\u636e\u5e93\u914d\u7f6e\u65e0\u6548\uff0c\u4f7f\u7528 database = 0\uff09
# sharded -> sharded servers (\u5bc6\u7801\u3001\u6570\u636e\u5e93\u5fc5\u987b\u5728 hosts \u4e2d\u6307\u5b9a\uff0c\u4e14\u8fde\u63a5\u6c60\u914d\u7f6e\u65e0\u6548 ; redis://user:password@127.0.0.1:6379/0\uff09
#
#########################################
redis.mode = single
#redis storage mode (generic|hash)
redis.storage = generic
## redis pub/sub channel name
redis.channel = j2cache
## redis pub/sub server (using redis.hosts when empty)
redis.channel.host =
#cluster name just for sharded
redis.cluster_name = j2cache
## redis cache namespace optional, default[empty]
redis.namespace =
## redis command scan parameter count, default[1000]
#redis.scanCount = 1000
## connection
# Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379
redis.hosts = 127.0.0.1:6379
redis.timeout = 2000
redis.password =
redis.database = 0
redis.ssl = false
## redis pool properties
redis.maxTotal = 100
redis.maxIdle = 10
redis.maxWaitMillis = 5000
redis.minEvictableIdleTimeMillis = 60000
redis.minIdle = 1
redis.numTestsPerEvictionRun = 10
redis.lifo = false
redis.softMinEvictableIdleTimeMillis = 10
redis.testOnBorrow = true
redis.testOnReturn = false
redis.testWhileIdle = true
redis.timeBetweenEvictionRunsMillis = 300000
redis.blockWhenExhausted = false
redis.jmxEnabled = false
#########################################
# Lettuce scheme
#
# redis -> single redis server
# rediss -> single redis server with ssl
# redis-sentinel -> redis sentinel
# redis-cluster -> cluster servers
#
#########################################
#########################################
# Lettuce Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (\u6570\u636e\u5e93\u914d\u7f6e\u65e0\u6548\uff0c\u4f7f\u7528 database = 0\uff09
# sharded -> sharded servers (\u5bc6\u7801\u3001\u6570\u636e\u5e93\u5fc5\u987b\u5728 hosts \u4e2d\u6307\u5b9a\uff0c\u4e14\u8fde\u63a5\u6c60\u914d\u7f6e\u65e0\u6548 ; redis://user:password@127.0.0.1:6379/0\uff09
#
#########################################
## redis command scan parameter count, default[1000]
#lettuce.scanCount = 1000
lettuce.mode = single
lettuce.namespace =
lettuce.storage = hash
lettuce.channel = j2cache
lettuce.scheme = redis
lettuce.hosts = 127.0.0.1:6379
lettuce.password =
lettuce.database = 0
lettuce.sentinelMasterId =
lettuce.maxTotal = 100
lettuce.maxIdle = 10
lettuce.minIdle = 10
# timeout in milliseconds
lettuce.timeout = 10000
# redis cluster topology refresh interval in milliseconds
lettuce.clusterTopologyRefresh = 3000
#########################################
# memcached server configurations
# refer to https://gitee.com/mirrors/XMemcached
#########################################
memcached.servers = 127.0.0.1:11211
memcached.username =
memcached.password =
memcached.connectionPoolSize = 10
memcached.connectTimeout = 1000
memcached.failureMode = false
memcached.healSessionInterval = 1000
memcached.maxQueuedNoReplyOperations = 100
memcached.opTimeout = 100
memcached.sanitizeKeys = false