Spring與Redis集成的正確方式流程詳解
1. 引入RedisTemplate
據以前的情況,我們在Java中使用Redis時一般是使用Jedis來操作的,大致的一段代碼如下所示
@Override public User findUserById(Integer id) { User user = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); String userStr = jedis.get("user_" + id); // 嘗試獲取數據 if (userStr != null && !userStr.isEmpty()) { // 如果獲取到有效數據,則轉換后返回 user = JSONObject.parseObject(userStr, User.class); } else {// 如果沒有獲取到數據,則查詢數據庫返回 user = userMapper.findUserById(id); if (user != null) jedis.set("user_" + id, JSONObject.toJSONString(user)); // 設置到redis中 } } finally { // 記得關閉Jedis,因為這里使用的是JedisPool,所以這里的關閉并不是直接關閉連接,而是釋放,以供其他的業(yè)務使用 if (jedis != null) jedis.close(); } return user; }
上邊的這樣的一段代碼其實是有些臃腫的,但是如果我們引入RedisTemplate,其實會簡化不少。
- maven 引入 spring-data-redis
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.2.13.RELEASE</version> </dependency>
- 將RedisTemplate 加入Bean容器中,讓Spring進行管理。
@Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); //設置key值的序列化方式,默認是JDK的形式 redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8); return redisTemplate; }
- 如果使用RedisTemplate的替換的話,會簡潔很多。
@Autowired private RedisTemplate redisTemplate; @Override public User findUserById(Integer id) { Object result = redisTemplate.opsForValue().get("user_" + id); if (result != null) return (User) result; User user = userMapper.findUserById(id); // 設置到redis中 if (user != null) redisTemplate.opsForValue().set("user_" + id, user); return user; }
大概看一下關于RedisTemplate的方法
看了以上的內容,可以看到引入了RedisTemplate其實已經很簡潔了,但是明顯還不夠,下面我們將考慮引入 “注解”
2. 引入注解
- 開啟緩存 @EnableCaching
AppConfig.java
@Configuration @EnableCaching public class AppConfig { ... }
- 引入@Cacheable,表示這個方法將會訪問緩存,如果無法命中緩存的話,會將方法返回的值存入redis,假設有注解為
@Cacheable(value="user", key = "#id")
,那么生成的key值為 user::{id},即如果id為1 那么生成的 key就是 user::1
@Override @Cacheable(value="user", key = "#id") // 這里返回的值會被存放到redis,key-value格式,其中生成的key值(假設id為1): user::1 public User findUserById(Integer id) { User user = userMapper.findUserById(id); return user; }
但是這樣還不夠,因為Spring并不清楚緩存的方式是什么,這就涉及到CacheManager
了
- 設置CacheManager,在AppConfig中加入以下內容
@Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); // 這里是redis的ip redisStandaloneConfiguration.setPort(port);// 這里是redis的端口 // 自適應集群變化 RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisCacheConfiguration redisCacheConfiguration() { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); return redisCacheConfiguration; } @Bean public RedisCacheWriter redisCacheWriter(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); return redisCacheWriter; } @Bean public CacheManager cacheManager(RedisCacheWriter redisCacheWriter, RedisCacheConfiguration redisCacheConfiguration) { CacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); ((RedisCacheManager) cacheManager).isTransactionAware(); return cacheManager; }
3. 擴展 - 自行通過注解和AOP實現緩存
- 引入AOP相關的包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.22</version> </dependency> <!-- Jackson JSON Processor --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version> </dependency>
- 創(chuàng)建@CustomCache
package cn.lazyfennec.cache.redis.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomCache { /** * key的規(guī)則,可以使用springEL表達式,可以使用方法執(zhí)行的一些參數 */ String key(); /** * 類似前綴 * @return */ String value(); }
- 修改AppConfig
@EnableAspectJAutoProxy // 開啟AOP自動代理 public class AppConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); // 自適應集群變化 RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8); redisTemplate.setValueSerializer(StringRedisSerializer.UTF_8); return redisTemplate; } }
- 創(chuàng)建 CustomCacheAspect
package cn.lazyfennec.cache.redis.annotation.aop; import cn.lazyfennec.cache.redis.annotation.CustomCache; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * @Author: Neco * @Description: * @Date: create in 2022/8/24 22:18 */ @Component @Aspect public class CustomCacheAspect { @Autowired private RedisTemplate redisTemplate; @Pointcut("@annotation(cn.lazyfennec.cache.redis.annotation.CustomCache)") public void cachePointcut() { } @Around("cachePointcut()") public Object doCache(ProceedingJoinPoint joinPoint) { Object obj = null; try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes()); CustomCache customCache = method.getAnnotation(CustomCache.class); String cacheKey = customCache.key(); String cacheValue = customCache.value(); // 創(chuàng)建解析器 ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(cacheKey); EvaluationContext context = new StandardEvaluationContext(); // 參數 // 添加參數 Object[] args = joinPoint.getArgs(); DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer(); String[] parameterNames = discover.getParameterNames(method); for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i].toString()); } // 解析 String key = cacheValue + "::" + expression.getValue(context).toString(); // 1、 判定緩存中是否存在 obj = redisTemplate.opsForValue().get(key); if (obj != null) return obj; // 2、不存在則繼續(xù)行方法 obj = joinPoint.proceed(); // 3、 同步存儲value到緩存。 redisTemplate.opsForValue().set(key, obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Throwable throwable) { throwable.printStackTrace(); } return obj; } }
- 新建方法 getUserNameById
@RequestMapping("/custom/name/{id}") @ResponseBody public String getUserNameById(@PathVariable Integer id) { return userService.getUserNameById(id); }
- 實際實現方法 getUserNameById,使用方式
@Override @CustomCache(value = "custom_user", key = "#id") public String getUserNameById(Integer id) { return userMapper.findUserNameById(id); }
以上就是Spring與Redis集成的正確方式詳解的詳細內容,更多關于Spring Redis集成方式的資料請關注腳本之家其它相關文章!
相關文章
詳解SpringBoot讀取resource目錄下properties文件的常見方式
這篇文章主要介紹了SpringBoot讀取resource目錄下properties文件的常見方式,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02Spring中使用Async進行異步功能開發(fā)實戰(zhàn)示例(大文件上傳為例)
本文以大文件上傳為例,首先講解在未進行程序異步化時,程序的運行機制和具體表現,然后講解如何進行異步化的改造,讓程序進行異步執(zhí)行,通過本文不僅能讓你掌握如何進行Event的事件開發(fā),同時還能掌握在Spring中如何進行異步開發(fā),熟悉@Async的具體用法,感興趣的朋友一起看看吧2024-08-08Springboot jar運行時如何將jar內的文件拷貝到文件系統(tǒng)中
因為執(zhí)行需要,需要把jar內templates文件夾下的的文件夾及文件加壓到宿主機器的某個路徑下,以便執(zhí)行對應的腳本文件,這篇文章主要介紹了Springboot jar運行時如何將jar內的文件拷貝到文件系統(tǒng)中,需要的朋友可以參考下2024-06-06