1、向pom文件中添加依赖
<!--springboot中的redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、application.yml添加配置
spring:
redis:
host: localhost # Redis服务器地址
database: 0 # Redis数据库索引(默认为0)
port: 6379 # Redis服务器连接端口
password: ld123456 # Redis服务器连接密码(默认为空)
3、RedisConfig配置类
package com.donleo.mybatis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.Arrays;
/**
* @author liangd
* date 2020-12-04 16:15
* code Redis配置类
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* 配置redisTemplate 代替默认配置
* @param redisConnectionFactory RedisConnectionFactory
* @return RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
/**
*
* 配置 cacheManager 代替默认的cacheManager (缓存管理器)
* @param factory RedisConnectionFactory
* @return CacheManager
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
/**
* 配置KeyGenerator
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
//lambda 表达式
return (target, method, params) -> method.getName() + Arrays.asList(params);
}
/*
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
*/
/* @Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params)->{
return method.getName()+ Arrays.asList(params);
};
}*/
}
4、自定义Redis工具类
1)接口层
package com.donleo.mybatis.service;
/**
* @author liangd
* date 2020-12-04 16:19
* code 自定义封装redis接口定义
*/
public interface IRedisService {
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
boolean expire(String key, long time);
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
long getExpire(String key);
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
boolean hasKey(String key);
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
void del(String... key);
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
Object get(String key);
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
boolean set(String key, Object value);
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
boolean set(String key, Object value, long time);
}
2)实现层
package com.donleo.mybatis.service.impl;
import com.donleo.mybatis.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.concurrent.TimeUnit;
/**
* @author liangd
* date 2020-12-04 16:21
* code 自定义封装Redis接口定义
*/
@Service
public class RedisServiceImpl implements IRedisService {
@Autowired
private RedisTemplate redisTemplate;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
@Override
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
@Override
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
@Override
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@Override
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
@Override
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
@Override
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
@Override
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
5、测试-->通过Redis工具类缓存
package com.donleo.mybatis.service.impl;
import com.donleo.mybatis.common.CommonResult;
import com.donleo.mybatis.dao.IDeptMapper;
import com.donleo.mybatis.model.Dept;
import com.donleo.mybatis.service.IDeptService;
import com.donleo.mybatis.service.IRedisService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author liangd
* date 2020-12-03 11:09
* code 部门逻辑层(使用自定义RedisTemplate设置缓存)
*/
@Service
public class DeptServiceImpl implements IDeptService {
@Resource
private IDeptMapper deptMapper;
@Resource
private IRedisService redisService;
@Override
public CommonResult findAll() {
List<Dept> list;
boolean b = redisService.hasKey("deptList:");
if (b) {
Object object = redisService.get("deptList:");
list = (List<Dept>) object;
} else {
list = deptMapper.selectAll();
redisService.set("deptList:",list);
}
return CommonResult.success(list);
}
@Override
public Integer add(Dept dept) {
dept.setId(null);
deptMapper.insert(dept);
return dept.getId();
}
@Override
public Integer delete(Integer id) {
redisService.del("dept:"+id);
redisService.del("deptList:");
return deptMapper.deleteByPrimaryKey(id);
}
@Override
public Integer update(Dept dept) {
return deptMapper.updateByPrimaryKeySelective(dept);
}
@Override
public Dept findById(Integer id) {
//判断redis中是否存在当前key,加一个冒号生成文件夹
boolean b = redisService.hasKey("dept:" + id);
Dept dept;
//如果存在,从redis中查询,否则从数据库中查询
if (b) {
dept = (Dept) redisService.get("dept:" + id);
} else {
dept = deptMapper.selectByPrimaryKey(id);
//放入resis中
redisService.set("dept:"+id, dept);
}
return dept;
}
}
6、测试-->通过CacheManager缓存(1)
package com.donleo.mybatis.service.impl;
import com.donleo.mybatis.common.CommonResult;
import com.donleo.mybatis.dao.IRoleMapper;
import com.donleo.mybatis.model.Role;
import com.donleo.mybatis.service.IRoleService;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author liangd
* date 2020-12-04 17:42
* code 角色逻辑实现层(使用注解CacheManager设置缓存)
* 使用@CacheConfig配置缓存名
*/
@Service
@CacheConfig(cacheNames = "role")
public class RoleServiceImpl implements IRoleService {
@Resource
private IRoleMapper roleMapper;
/**
* 使用@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。
* 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
*
* @param role
* @return
*/
@Override
@Caching(
put = @CachePut(key = "#role.id"),
evict = @CacheEvict(key = "'findAllRole[]'")
)
public CommonResult add(Role role) {
role.setId(null);
try {
roleMapper.insert(role);
} catch (Exception e) {
return CommonResult.failed();
}
return CommonResult.success(role.getId());
}
/**
* 使用@CacheEvict移除key
* @param id
* @return
*/
@Override
@CacheEvict(key = "#id")
public Integer delete(Integer id) {
return roleMapper.deleteByPrimaryKey(id);
}
@Override
public Integer update(Role role) {
return roleMapper.updateByPrimaryKeySelective(role);
}
/**
* 使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,
* 如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
*
* @param id
* @return
*/
@Override
@Cacheable(key = "#id")
public Role findById(Integer id) {
return roleMapper.selectByPrimaryKey(id);
}
/**
* 使用@CachePut 也可以声明一个方法支持缓存功能。
* 与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,
* 而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
*
* @return
* keyGenerator将方法名作为key,存进缓存,例如findAllRole[],删除key时需要添加单引号
*/
@Override
@CachePut(keyGenerator = "keyGenerator")
public CommonResult findAllRole() {
List<Role> list = roleMapper.selectAll();
return CommonResult.success(list);
}
}
7、测试-->通过CacheManager缓存(2)
package com.donleo.cache.service.impl;
import com.donleo.cache.common.CommonResult;
import com.donleo.cache.mapper.IRoleMapper;
import com.donleo.cache.model.Role;
import com.donleo.cache.service.IRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author liangd
* date 2020-12-10 09:49
* code
* .@CacheConfig抽取缓存的公共配置,在这里配置了cacheNames就不需要再每个方法上面指定value属性了
*/
@Service
@CacheConfig(cacheNames = "role")
public class RoleServiceImpl implements IRoleService {
@Autowired
private IRoleMapper roleMapper;
/**
* .@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
* 如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
*
* .@Cacheable 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法;
* <p>
* CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一-个缓存组件有自己唯- --一个名字;
* <p>
* <p>
* 原理:
* 1、自动配置类 CacheAutoConfiguration
* 2、缓存的配置类
* org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
* org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
* org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
* org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
* org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
* org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
* org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
* <p>
* 3、哪个配置类默认生效:SimpleCacheConfiguration;
* <p>
* 4、给容器中注册了一个CacheManager:ConcurrentMapCacheManager
* 5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;
* <p>
* 运行流程:
* <p>
* .@Cacheable:
* 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
* (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
* 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
* key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
* SimpleKeyGenerator生成key的默认策略;
* 如果没有参数;key=new SimpleKey();
* 如果有一个参数:key=参数的值
* 如果有多个参数:key=new SimpleKey(params);
* 3、没有查到缓存就调用目标方法;
* 4、将目标方法返回的结果,放进缓存中
* <p>
* <p>
* 核心:
* 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
* 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
* <p>
* 几个属性:
* a)cacheNames/value:指定缓存组件的名字
* cacheNames = {"role"}可以使用多个参数,是数组的形式,可以指定多个缓存
* b)key:缓存数据使用的key,可以用他来指定。默认是使用方法参数的值
* 编写SpEl: #id #a0,#po,#argrs[0] "0"代表参数的索引
* #result 方法执行后的返回值
* #root.methodName 方法名
* key = "#root.methodName+'['+#id+']'"
* c)keyGenerator: key的生成器、可以自己指定key的生成器的组件Id
* key/keyGenerator 二选一
* keyGenerator = "myKeyGenerator"
* d)cacheManager:指定缓存管理器或者cacheResolver:获取解析器
* cacheManager/cacheResolver 二选一
* e)condition:指定符合缓存的条件
* condition = "#id>0 and #root.methodName eq 'aaa'" 可以多条件判断
* f)unless: 否定缓存,当unless的条件为true,方法结果不会被缓存,可以获取结果进行判断
* unless = "#result==null",结果为null,就不缓存
* g)sync:是否使用异步模式
* 默认false 同步
* 为true时,unless不支持
*/
@Override
@Cacheable(value = {"role"}, key = "#id",
condition = "#id>0", unless = "#result==null")
public Role findById(Integer id) {
return roleMapper.selectById(id);
}
/**
* .@CachePut既调用方法、又更新数据,达到同步更新缓存
* <p>
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
*
* 条件:存取Id的key要保持一致
* key = "#role.id" 传入员工的Id
* key = "#result.id" 使用返回员工的Id
* 注意: @Cacheable不能使用#result
* 因为 @Cacheable在目标方法执行之前需要得到这个key,所以不能用#result
*/
@Override
@CachePut(value = "role", key = "#result.id")
public Role update(Role role) {
roleMapper.updateById(role);
return role;
}
/**
* .@CacheEvict 缓存清除
*
* key:指定要清除的数据
* allEntries:指定清除这个缓存库的所有数据,默认为false
* beforeInvocation:在执行方法之前清除,默认为false,在方法之后执行
*
*/
@Override
@CacheEvict(/*value = "role",*/key = "#id")
public Integer delete(Integer id) {
return roleMapper.deleteById(id);
}
/**
* .@Caching 定义复杂缓存规则
*/
@Override
@Caching(
cacheable = {
@Cacheable(key = "#role.roleName")
},
put = {
@CachePut(key = "#role.id"),
@CachePut(key = "#role.roleCode")
}
)
public CommonResult add(Role role) {
role.setId(null);
try {
roleMapper.insert(role);
} catch (Exception e) {
return CommonResult.failed();
}
return CommonResult.success(role.getId());
}
@Override
public CommonResult findAllRole() {
List<Role> roleList = roleMapper.selectList(null);
return CommonResult.success(roleList);
}
}