专业的编程技术博客社区

网站首页 > 博客文章 正文

自定义SpringCache(自定义springencoder)

baijin 2024-09-11 00:34:27 博客文章 10 ℃ 0 评论

自定义注解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
}

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表