1.缓存雪崩
1.1解决思路
1.2 解决办法
如果每一个方法都像上面这样处理,工作量会很大,这时可以使用自定义注解AOP减少工作量
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
// 开启AOP
@EnableAspectJAutoProxy
class CustomRedisAppConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
System.out.println("使用自定义注解版本");
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 可以配置对象的转换规则,比如使用json格式对object进行存储。
// Object --> 序列化 --> 二进制流 --> redis-server存储
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* cache注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
/**
* key的规则,可以使用springEL表达式,可以使用方法执行的一些参数
*/
String key();
}
import com.study.cache.redis.a8_custom.annotations.Cache;
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;
@Component
@Aspect
public class CacheAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.study.cache.redis.a8_custom.annotations.Cache)")
public void cachePointcut() {
}
// 定义相应的事件
@Around("cachePointcut()")
public Object doCache(ProceedingJoinPoint joinPoint) {
Object value = null;
try {
// 0-1、 当前方法上注解的内容
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
Cache cacheAnnotation = method.getAnnotation(Cache.class);
String keyEl = cacheAnnotation.key();
// 0-2、 前提条件:拿到作为key的依据 - 解析springEL表达式
// 创建解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyEl);
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 = expression.getValue(context).toString();
// 1、 判定缓存中是否存在
value = redisTemplate.opsForValue().get(key);
if (value != null) {
System.out.println("从缓存中读取到值:" + value);
return value;
}
// 2、不存在则执行方法
value = joinPoint.proceed();
// 3、 同步存储value到缓存。
redisTemplate.opsForValue().set(key, value);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return value;
}
}
使用方法
@Cache(key = "#userId")
public User findUserById(String userId) throws Exception {
User user = null;
user = new User(userId, "张三");
System.out.println("从数据库中读取到值:" + user);
return user;
}