# 【Java】SpringBoot 整合 Redis 对查询数据做缓存( 利用 Spring 的 AOP 技术)
# 本篇主要介绍 SpringBoot 整合 Redis 做数据缓存,利用的是 SpringAop 切面编程技术,利用注解标识切面。
# 这里不再介绍 spring boot 操作数据库,有兴趣的话,我最后会给出源码链接
# 一,引入依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> <version>1.3.2.RELEASE</version> </dependency>12345678910
|
# 二,配置 redis 连接
配置文件我里这用的是 yml 格式的,tab 缩进,如果是 properties 格式的,请自己改造
redis 安装请参考 Redis 安装
windows 管理工具可以用 RedisDesktopManager,测试的时候,可以直接删除缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: redis: database: 0 host: 127.0.0.1.128 port: 6379 password: timeout: 0 pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 01234567891011121314151617181920
|
# 三,注解
注解 QueryCache 用来标识查询数据库的方法,参数 nameSpace 用来区分应用的,后面会用来添加到缓存的 key 中。比如,登陆应用缓存的数据 key 值全部都是 sso 开头。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.example.common.annotation;
import com.example.common.CacheNameSpace;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface QueryCache { CacheNameSpace nameSpace(); }
|
注解 QueryCacheKey 是方法级别的注解,用来标注要查询数据的主键,会和上面的 nameSpace 组合做缓存的 key 值
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.example.common.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @Documented public @interface QueryCacheKey { }
|
枚举 CacheNameSpace 用来保存 nameSpace 的
1 2 3 4 5 6 7 8
| package com.example.common;
public enum CacheNameSpace { SSO_USER }
|
下面就是组合起来的用法,userMapper.findById (id) 是用来查询数据库的方法
1 2 3 4 5 6 7
| @QueryCache(nameSpace = CacheNameSpace.SSO_USER) public UserInfo findUserById(@QueryCacheKey Long id) {
UserInfo userInfo = userMapper.findById(id);
return userInfo; }
|
# 四,Aop 切面
下面是重点,代码中的注释已经很多了,应该能看的懂,如有问题,可以留言。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package com.example.common.aspect;
import com.example.common.CacheNameSpace; import com.example.common.annotation.QueryCache; import com.example.common.annotation.QueryCacheKey; import org.apache.commons.lang3.StringUtils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit;
@Aspect @Service public class DBCacheAspect { private static final Logger LOGGER = LoggerFactory.getLogger(DBCacheAspect.class);
@Resource private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.example.common.annotation.QueryCache)") public void queryCachePointcut(){}
@Around("queryCachePointcut()") public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable { long beginTime = System.currentTimeMillis(); LOGGER.info("AOP 缓存切面处理 >>>> start "); MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); CacheNameSpace cacheType = method.getAnnotation(QueryCache.class).nameSpace(); String key = null; int i = 0;
for (Object value : pjp.getArgs()) { MethodParameter methodParam = new SynthesizingMethodParameter(method, i); Annotation[] paramAnns = methodParam.getParameterAnnotations();
for (Annotation paramAnn : paramAnns) { if ( paramAnn instanceof QueryCacheKey) { QueryCacheKey requestParam = (QueryCacheKey) paramAnn; key = cacheType.name() + "_" + value; } } i++; }
if (StringUtils.isBlank(key)) throw new Exception("缓存key值不存在");
LOGGER.info("获取到缓存key值 >>>> " + key); ValueOperations<String, Object> operations = redisTemplate.opsForValue(); boolean hasKey = redisTemplate.hasKey(key); if (hasKey) {
Object object = operations.get(key); LOGGER.info("从缓存中获取到数据 >>>> " + object.toString()); LOGGER.info("AOP 缓存切面处理 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime));
return object; }
Object object = pjp.proceed(); operations.set(key, object, 30, TimeUnit.MINUTES);
LOGGER.info("DB取到数据并存入缓存 >>>> " + object.toString()); LOGGER.info("AOP 缓存切面处理 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime)); return object; }
}
|
# 五,测试
1 2 3 4
| @Test public void testFindById(){ UserInfo userInfo = userService.findUserById(210001L); }
|
# 六,执行结果
两张图对比一下很明显,从 redis 缓存中取数据耗时要少的多。
# 关于我
Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!
非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!