Spring與Redis集成的正確方式流程詳解
1. 引入RedisTemplate
據(jù)以前的情況,我們在Java中使用Redis時(shí)一般是使用Jedis來操作的,大致的一段代碼如下所示
@Override
public User findUserById(Integer id) {
User user = null;
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String userStr = jedis.get("user_" + id); // 嘗試獲取數(shù)據(jù)
if (userStr != null && !userStr.isEmpty()) { // 如果獲取到有效數(shù)據(jù),則轉(zhuǎn)換后返回
user = JSONObject.parseObject(userStr, User.class);
} else {// 如果沒有獲取到數(shù)據(jù),則查詢數(shù)據(jù)庫返回
user = userMapper.findUserById(id);
if (user != null) jedis.set("user_" + id, JSONObject.toJSONString(user)); // 設(shè)置到redis中
}
} finally {
// 記得關(guān)閉Jedis,因?yàn)檫@里使用的是JedisPool,所以這里的關(guān)閉并不是直接關(guān)閉連接,而是釋放,以供其他的業(yè)務(wù)使用
if (jedis != null) jedis.close();
}
return user;
}上邊的這樣的一段代碼其實(shí)是有些臃腫的,但是如果我們引入RedisTemplate,其實(shí)會(huì)簡化不少。
- 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進(jìn)行管理。
@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);
//設(shè)置key值的序列化方式,默認(rèn)是JDK的形式
redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
return redisTemplate;
}
- 如果使用RedisTemplate的替換的話,會(huì)簡潔很多。
@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);
// 設(shè)置到redis中
if (user != null) redisTemplate.opsForValue().set("user_" + id, user);
return user;
}
大概看一下關(guān)于RedisTemplate的方法

看了以上的內(nèi)容,可以看到引入了RedisTemplate其實(shí)已經(jīng)很簡潔了,但是明顯還不夠,下面我們將考慮引入 “注解”
2. 引入注解
- 開啟緩存 @EnableCaching
AppConfig.java
@Configuration
@EnableCaching
public class AppConfig {
...
}
- 引入@Cacheable,表示這個(gè)方法將會(huì)訪問緩存,如果無法命中緩存的話,會(huì)將方法返回的值存入redis,假設(shè)有注解為
@Cacheable(value="user", key = "#id"),那么生成的key值為 user::{id},即如果id為1 那么生成的 key就是 user::1
@Override
@Cacheable(value="user", key = "#id")
// 這里返回的值會(huì)被存放到redis,key-value格式,其中生成的key值(假設(shè)id為1): user::1
public User findUserById(Integer id) {
User user = userMapper.findUserById(id);
return user;
}
但是這樣還不夠,因?yàn)镾pring并不清楚緩存的方式是什么,這就涉及到CacheManager 了
- 設(shè)置CacheManager,在AppConfig中加入以下內(nèi)容
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host); // 這里是redis的ip
redisStandaloneConfiguration.setPort(port);// 這里是redis的端口
// 自適應(yīng)集群變化
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. 擴(kuò)展 - 自行通過注解和AOP實(shí)現(xiàn)緩存
- 引入AOP相關(guān)的包
<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表達(dá)式,可以使用方法執(zhí)行的一些參數(shù)
*/
String key();
/**
* 類似前綴
* @return
*/
String value();
}
- 修改AppConfig
@EnableAspectJAutoProxy // 開啟AOP自動(dòng)代理
public class AppConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
// 自適應(yīng)集群變化
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(); // 參數(shù)
// 添加參數(shù)
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、 同步存儲(chǔ)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);
}
- 實(shí)際實(shí)現(xiàn)方法 getUserNameById,使用方式
@Override
@CustomCache(value = "custom_user", key = "#id")
public String getUserNameById(Integer id) {
return userMapper.findUserNameById(id);
}以上就是Spring與Redis集成的正確方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Redis集成方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解SpringBoot讀取resource目錄下properties文件的常見方式
這篇文章主要介紹了SpringBoot讀取resource目錄下properties文件的常見方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
SpringBoot中默認(rèn)緩存實(shí)現(xiàn)方案的示例代碼
這篇文章主要介紹了SpringBoot中默認(rèn)緩存實(shí)現(xiàn)方案,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式
Java WebSocket是一種基于TCP協(xié)議的雙向全雙工消息傳輸技術(shù),它允許服務(wù)器和客戶端之間實(shí)時(shí)通信,具有低延遲和高效率的特點(diǎn),下面這篇文章主要給大家介紹了關(guān)于java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式,需要的朋友可以參考下2024-08-08
Spring中使用Async進(jìn)行異步功能開發(fā)實(shí)戰(zhàn)示例(大文件上傳為例)
本文以大文件上傳為例,首先講解在未進(jìn)行程序異步化時(shí),程序的運(yùn)行機(jī)制和具體表現(xiàn),然后講解如何進(jìn)行異步化的改造,讓程序進(jìn)行異步執(zhí)行,通過本文不僅能讓你掌握如何進(jìn)行Event的事件開發(fā),同時(shí)還能掌握在Spring中如何進(jìn)行異步開發(fā),熟悉@Async的具體用法,感興趣的朋友一起看看吧2024-08-08
Springboot jar運(yùn)行時(shí)如何將jar內(nèi)的文件拷貝到文件系統(tǒng)中
因?yàn)閳?zhí)行需要,需要把jar內(nèi)templates文件夾下的的文件夾及文件加壓到宿主機(jī)器的某個(gè)路徑下,以便執(zhí)行對(duì)應(yīng)的腳本文件,這篇文章主要介紹了Springboot jar運(yùn)行時(shí)如何將jar內(nèi)的文件拷貝到文件系統(tǒng)中,需要的朋友可以參考下2024-06-06

