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); } }
