网站首页 > 博客文章 正文
自定义注解CacheableUser
该注解联动用户ID和手机号来添加缓存
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用户缓存添加联动处理
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
public @interface CacheableUser {
String userId() default "";
String phone() default "";
}
自定义注解CacheEvictUser
该注解联动用户ID和手机号来清理缓存
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 学校用户缓存失效联动处理
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
public @interface CacheEvictUser {
String userId() default "";
String phone() default "";
}
实现注解Aspect类
该类实现@Pointcut,在方法上使用@CacheEvictUser 或者@CacheableUser的注解。
分别使用@After和@Around的方式作用于切入点织人缓存相关代码。
@Aspect
@Component
public class CacheUserAspect {
private static final String CACHE_NAME = "user_cache";
@Autowired
private ExpressionBeanResolver beanResolver;
@Autowired
private RedisCacheManager cacheManager;
/**
* User缓存失效Pointcut
*/
@Pointcut("@annotation(com.xxx.user.impl.cacheevict.CacheEvictUser)")
public void pointCutCacheEvict() {
}
/**
* User缓存写入Pointcut
*/
@Pointcut("@annotation(com.xxxx.user.impl.cacheevict.CacheableUser)")
public void pointCutCacheable() {
}
/**
* 缓存联动失效
*
* @param joinPoint 缓存失效方法joinPoint
*/
@After("pointCutCacheEvict()")
public void cacheEvictUser(JoinPoint joinPoint) {
Method method = SpelReseover.getMethod(joinPoint);
EvaluationContext context = SpelReseover.createEvaluationContext(joinPoint, beanResolver);
CacheEvictUser annotation = method.getDeclaredAnnotation(CacheEvictUser.class);
String phoneSpel = annotation.phone();
String userIdSpel = annotation.userId();
if (StringUtils.isEmpty(phoneSpel) && StringUtils.isEmpty(userIdSpel)) {
return;
}
Integer userId = null;
String phone = null;
if (!StringUtils.isEmpty(userIdSpel)) {
userId = SpelReseover.parseExpression(context, userIdSpel, Integer.class);
}
if (!StringUtils.isEmpty(phoneSpel)) {
phone = SpelReseover.parseExpression(context, phoneSpel, String.class);
}
doEvictCache(userId, phone);
}
/**
* User缓存联动添加获取
*
* @param joinPoint User缓存添加joinPoint
* @return
*/
@Around("pointCutCacheable()")
public Object cacheableUser(ProceedingJoinPoint joinPoint) {
Method method = SpelReseover.getMethod(joinPoint);
EvaluationContext context = SpelReseover.createEvaluationContext(joinPoint, beanResolver);
CacheableUser annotation = method.getDeclaredAnnotation(CacheableUser.class);
String phoneSpel = annotation.phone();
String userIdSpel = annotation.userId();
if (StringUtils.isEmpty(phoneSpel) && StringUtils.isEmpty(userIdSpel)) {
return proceedJoinPoint(joinPoint);
}
Integer userId = null;
String phone = null;
if (!StringUtils.isEmpty(userIdSpel)) {
userId = SpelReseover.parseExpression(context, userIdSpel, Integer.class);
}
if (!StringUtils.isEmpty(phoneSpel)) {
phone = SpelReseover.parseExpression(context, phoneSpel, String.class);
}
//方法执行前尝试从缓存获取数据
RedisCache cache = (RedisCache) cacheManager.getCache(CACHE_NAME);
User obj = null;
if (userId != null) {
obj = cache.get(userId, User.class);
} else if (!StringUtils.isEmpty(phone)) {
obj = cache.get(phone, User.class);
}
if (obj != null) {
return obj;
}
//执行方法
Object exeObj = proceedJoinPoint(joinPoint);
if (exeObj != null) {
obj = (User) exeObj;
if (obj.getUserId() != null) {
cache.put(obj.getUserId(), obj);
}
if (!StringUtils.isEmpty(obj.getPhone())) {
cache.put(obj.getPhone(), obj);
}
}
return exeObj;
}
/**
* 根据userId和phone使缓存全部失效
* @param userId 用户ID
* @param phone 手机号
*/
private void doEvictCache(Integer userId, String phone) {
if (userId == null && StringUtils.isEmpty(phone)) {
return;
}
try {
RedisCache cache = (RedisCache) cacheManager.getCache(CACHE_NAME);
//userId和phone参数都有值,直接清空缓存
if (userId != null && !StringUtils.isEmpty(phone)) {
cache.evict(userId);
cache.evict(phone);
return;
}
if (userId != null && cache.get(userId) != null) {
//根据userId参数,清空缓存
Object suObj = cache.get(userId).get();
if (suObj != null) {
User user = (User) suObj;
phone = user.getPhone();
if (phone != null) {
cache.evict(phone);
}
}
cache.evict(userId);
} else if (!StringUtils.isEmpty(phone) && cache.get(phone) != null) {
//根据phone参数,清空缓存
Object suObj = cache.get(phone).get();
if (suObj != null) {
User user = (User) suObj;
userId = user.getUserId();
if (userId != null) {
cache.evict(userId);
}
}
cache.evict(phone);
}
} catch (Exception ex) {
//log.error("缓存失效异常", ex);
}
}
/**
* 执行连接点
*/
private Object proceedJoinPoint(ProceedingJoinPoint point) {
try {
return point.proceed();
} catch (Throwable throwable) {
throw new RuntimeException(throwable.getMessage());
}
}
}
ExpressionBeanResolver类代码
@Service
public class ExpressionBeanResolver implements BeanResolver, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException {
return this.beanFactory.getBean(beanName);
}
}
SpelReseover类代码
/**
* spel表达式解析器
*/
public class SpelReseover {
private static LocalVariableTableParameterNameDiscoverer parameterNameDiscovere =
new LocalVariableTableParameterNameDiscoverer();
private static ExpressionParser parser = new SpelExpressionParser();
//缓存SPEL Expression
private static Map<String, Expression> spelExpressionCaches = new ConcurrentHashMap<>();
//缓存有@RedisLockable方法参数名称
private static Map<String, String[]> parameterNameCaches = new ConcurrentHashMap<>();
/**
* 解析切点表达式的值
*
* @param joinPoint
* @param beanResolver
* @param expStr
* @param isTemplate
* @return
*/
public static String parseExpression(JoinPoint joinPoint, ExpressionBeanResolver beanResolver,
String expStr, boolean isTemplate) {
EvaluationContext context = createEvaluationContext(joinPoint, beanResolver);
return parseExpression(context, expStr, isTemplate);
}
/**
* 创建表达式解析上下文
*
* @param joinPoint 连接点
* @return
*/
public static EvaluationContext createEvaluationContext(JoinPoint joinPoint, ExpressionBeanResolver beanResolver) {
// 获取方法中的参数名数组
String methodLongName = joinPoint.getSignature().toLongString();
String[] parameterNames = parameterNameCaches.get(methodLongName);
if (parameterNames == null) {
Method method = getMethod(joinPoint);
parameterNames = parameterNameDiscovere.getParameterNames(method);
parameterNameCaches.put(methodLongName, parameterNames);
}
StandardEvaluationContext context = new StandardEvaluationContext();
// 设置beanResolver,用于解析表达式时获取表达式中以@符号引用的bean
context.setBeanResolver(beanResolver);
//将方法的所有参数设置到表达式估值上下文
Object[] args = joinPoint.getArgs();
if (args.length == parameterNames.length) {
for (int i = 0, len = args.length; i < len; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
return context;
}
/**
* 解析spring表达式
*
* @param context
* @param expStr
* @param returnType
* @param <T>
* @return
*/
public static <T> T parseExpression(EvaluationContext context, String expStr, Class<T> returnType) {
if (StringUtils.isEmpty(expStr)) {
return null;
}
Expression expression = parser.parseExpression(expStr);
return expression.getValue(context, returnType);
}
/**
* 解析spring表达式或模板
*
* @param expStr 表达式或模板
* @param isTemplate expStr是否模板
* @return 表达式对应的字符串
*/
public static String parseExpression(EvaluationContext context,
String expStr, boolean isTemplate) {
//缓存表达式
Expression expression = spelExpressionCaches.get(expStr);
if (expression == null) {
if (isTemplate) {
expression = parser.parseExpression(expStr, new TemplateParserContext());
} else {
expression = parser.parseExpression(expStr);
}
spelExpressionCaches.put(expStr, expression);
}
String result = null;
if (expression instanceof CompositeStringExpression) {
// 如果是复合表达式,则遍历所有表达式,然后把表达式的值组成一个字符串
result = "";
for (Expression subExpression : ((CompositeStringExpression) expression).getExpressions()) {
Object o = subExpression.getValue(context);
// 如果对字符串用JSON.toJSONString序列化,返回结果会包含双引号
result += o instanceof String ? o.toString() : JSON.toJSONString(o);
}
} else {
// 如果是单个表达式(LiteralExpression, SPELExpression),取出表达式的值,然后序列化成字符串
Object o = expression.getValue(context);
// 如果对字符串用JSON.toJSONString序列化,返回结果会包含双引号
result = o instanceof String ? o.toString() : JSON.toJSONString(o);
}
return result;
}
/**
* 获得切点方法
*
* @param joinPoint
* @return
*/
public static Method getMethod(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
return methodSignature.getMethod();
}
}
自定义注解的使用
根据手机号查询用户并添加到缓存中和更新用户使用户缓存失效
@Override
@CacheableUser(phone = "#phone")
public User getUserByPhone(String phone) {
User user = userMapper.getUserByPhone(phone);
return user;
}
@Override
@CacheEvictUser(userId = "#user.userId", phone = "#user.phone")
public boolean updateAuthUser(User user) {
// code here
}
猜你喜欢
- 2024-09-11 Spring Security 全局方法安全:预过滤和后过滤(2)
- 2024-09-11 「Spring Boot 源码研究 」- 自动化装配条件化配置Conditional剖析
- 2024-09-11 面试:Spring Boot 中的条件注解底层是如何实现的?
- 2024-09-11 SpringBoot系列(十五)整合缓存,项目必用的技术
- 2024-09-11 如何优雅地记录操作日志?(如何优雅地记录操作日志)
- 2024-09-11 SpEL应用实战(应用spc技术)
- 2024-09-11 有趣的SpEL注入(有趣的工作群名称大全)
- 2024-09-11 SpringBoot 实现异步记录复杂日志
- 2024-09-11 redis 分布式锁(redis 分布式锁失效时间)
- 2024-09-11 CVE-2022-22947分析(cve-2020-0796分析)
你 发表评论:
欢迎- 06-23MySQL合集-mysql5.7及mysql8的一些特性
- 06-23MySQL CREATE TABLE 简单设计模板交流
- 06-23MYSQL表设计规范(mysql设计表注意事项)
- 06-23MySQL数据库入门(四)数据类型简介
- 06-23数据丢失?别慌!MySQL备份恢复攻略
- 06-23MySQL设计规范(mysql 设计)
- 06-23MySQL数据实时增量同步到Elasticsearch
- 06-23MySQL 避坑指南之隐式数据类型转换
- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)