在上一章的基础上 。升级代码。
第一步加入 AOP
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>```
第二步 编写注解方法
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface MyRedis {
String value() ;
long expire() default -1;
}
第三步,编写AOP 处理方法
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class RedisHandle {
@Autowired
RedisTemplate redisTemplate;
@Around("@annotation(myRedis)")
public Object aroundMethod(ProceedingJoinPoint pjd, MyRedis myRedis) throws Throwable {
Object[] args = pjd.getArgs();
String key = myRedis.value();
for (int i = 0; i < args.length; i++) {
String replace = "#{" + i + "}";
key = key.replace(replace, String.valueOf(args[i]));
}
ValueOperations valueOperations = redisTemplate.opsForValue();
Object value = valueOperations.get(key);
//redis 热点问题
//服务器多线程并发访问时,可能会有多个线程同时访问为空,都会去执行设置操作,
if (value == null) {
//可能有多个线程阻塞在这里,当synchronized执行完成之后。
synchronized (this){
value = valueOperations.get(key);
//每个线程进入后,发现如果已经有值了,就不再执行set操作
if(value==null){
value = pjd.proceed();
valueOperations.set(key, value, myRedis.expire(), TimeUnit.SECONDS);
}
}
}
return value;
}
}
@MyRedis(value = "userkey#{0}", expire = 60 * 60 * 6)
public User getUser(Long id) {
... 业务代码....
return user;
}
@Override
//这里的 value 是用于 redis 的可以 组拼方式
//{1} 代表第二个参数来拼 {0} 就是第一个参数 举例如下:
// userKey#{1} == userKey + name
// userKey#{0} == userKey + id
@MyRedis(value = "userKey#{1}", expire = 10)
public User getUser(String id, String name) {
System.out.println("读取数据库");
return userDao.getUser(id);
}