总结
- SpringCache定义了@Cacheable、@CachePut、@CacheEvict三个注解,分别对应查询、更新、删除时对缓存的处理
- 只需要在Server类添加该注解,就可以通过AOP自动代理实现缓存查询、缓存更新、缓存删除,使用形式如下,@Cacheable、@CachePut会在unless条件成立时将返回值添加进缓存,而@Cacheable在有缓存时不会执行Server类方法。@CacheEvict会直接删除缓存名为value的缓存中的键值对的键为key的键值对
@Override
@Cacheable(value = "userCache", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
System.out.println("运行");
return userMapper.selectById(id);
}
@Override
@CachePut(value = "userCache", key = "#user.id", unless = "#result == null")
public User updateUser(User user) {
userMapper.updateById(user);
return userMapper.selectById(user.getId());
}
@Override
@CacheEvict(value = "userCache", key = "#id")
public void deleteUserById(Long id) {
userMapper.deleteById(id);
}
-
缓存的name为userCache,也就是下面接口方法对应的缓存名字,key就是通过name获取后,该缓存类似Map结构,可以根据key存储值和取出值,@Cacheable也是根据每次需要执行的代码key是否在缓存中存在来决定是否执行,存在则不执行
-
而SpringCache通过注解实现缓存实际上的逻辑是在CacheManager和Cache中定义的,在CacheManager使用缓存名字value,获取缓存Cache,然后缓存Cache中定义了对键值对增删查改的方法,通过这种接口抽象,可以自定义缓存,也可以整合第三方缓存,如本地缓存Caffeine和分布式缓存Redisson
自己实现SpringCache的功能
项目地址
https://gitee.com/chen_yan_ting/springboot_cache
项目结构
自定义缓存
定义如下三个注解, @MyCacheable, @MyCacheable
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheable {
String value();
String key();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCachePut {
String value();
String key();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheEvict {
String value();
String key();
}
AOP实现类
- 使用ProceedingJoinPoint获得被代理类的类对象,进而获取其方法列表Method[]
- 通过ProceedingJoinPoint的Signature可以获得被代理类运行的方法名,通过方法名在Method[]找到目标方法targetMethod
- 获取目标方法targetMethod上的@MyCacheable注解,再获取注解的value对应缓存的名字,获取key对应缓存中键值对的键,一个value对应一个缓存即一个Map,一个key对应Map中的键值对
- 再通过LocalVariableTableParameterNameDiscoverer获取方法参数名字,将key从名字替换成具体的参数值arg
- 通过value从cacheManager获取缓存cache,再通过具体的参数值arg作为键key往Map中添加键值对,如果存在键值对则无须执行原来的方法,直接返回缓存的值
@Component
@Aspect
public class CacheableAspact {
@Autowired
private CacheManager cacheManager;
@Pointcut("@annotation(com.lolxxs.annotation.MyCacheable)")
public void pointcut() {
}
@Around("pointcut()")
public Object round(ProceedingJoinPoint point) {
System.out.println("代理类运行1");
// 获取被代理类的签名
Signature signature = point.getSignature();
// 获取被代理类对象
Object target = point.getTarget();
// 获取被代理类class对象
Class declaringType = signature.getDeclaringType();
// 获取类中的方法
Method[] methods = declaringType.getMethods();
Method targetMethod = null;
for (Method method : methods) {
// 如果该方法名和AOP代理切入点方法名相同
if (method.getName().equals(signature.getName())) {
targetMethod = method;
break;
}
}
Cache cache = null;
Object arg = null;
if (targetMethod != null) {
MyCacheable myCacheable = (MyCacheable)targetMethod.getAnnotation(MyCacheable.class);
if (myCacheable != null) {
// 获取缓存名
String value = myCacheable.value();
// 获取缓存中的键,其实是参数名
String key = myCacheable.key();
cache = cacheManager.getCache(value);
// 获取方法的参数列表
Parameter[] parameters = targetMethod.getParameters();
Object[] args = point.getArgs();
LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
// 获取方法名称列表
String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(targetMethod);
//遍历方法名称列表
int i = 0;
for (String parameter: parameterNames) {
if (parameter.equals(key)) {
//找到指定名称的参数
arg = args[i];
}
i++;
}
if (cache != null && arg != null) {
Cache.ValueWrapper valueWrapper = cache.get(arg);
if (valueWrapper != null) {
return valueWrapper.get();
}
}
}
}
// 正常执行原本方法逻辑
Object proceed = null;
Object[] args = point.getArgs();
try {
proceed = point.proceed(args);
if (cache != null && arg != null) {
cache.put(arg, proceed);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
}
自定义缓存实现类
主要实现官方提供的两个接口CacheManager接口和Cache接口
可以看我这篇文章 https://blog.csdn.net/weixin_45754452/article/details/123942273
使用
直接对Server类进行AOP切面, @MyCacheable走缓存则不执行指定方法对应GET操作,@MyCachePut必须执行指定方法且会更新缓存对应Update操作, @MyCacheEvict一定会执行指定方法,且会删除缓存对应Delete操作
@Service("userServiceImpl")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@MyCacheable(value = "userCache", key = "id")
public User getUserById(Long id) {
System.out.println("运行");
return userMapper.selectById(id);
}
@MyCachePut(value = "userCache", key = "id")
public User updateUser(Long id, User user) {
userMapper.updateById(user);
return userMapper.selectById(user.getId());
}
@MyCacheEvict(value = "userCache", key = "id")
public void deleteUserById(Long id) {
userMapper.deleteById(id);
}
}