spring aop 自定义注解实现本地缓存

1.首先加入本地缓存依赖这里用到的是caffine

<!--本地缓存 --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-cache</artifactId></dependency>

<dependency>    <groupId>com.github.ben-manes.caffeine</groupId>    <artifactId>caffeine</artifactId>    <version>2.7.0</version></dependency>

2. 进行缓存配置管,(这里每一个接口都是缓存2秒,不要考虑缓存更新,同时也可以防止缓存过度使用导致内存泄漏,最主要的是防止恶意攻击接口和瞬时并发,直接拉崩数据库,如果需要单独对每一个接口的缓存时间进行单独配置需要配置CacheManager)
@Configurationpublic class CacheConfig {    @Bean    public Cache<String, Object> caffeineCache() {        return Caffeine.newBuilder()                // 设置最后一次写入或访问后经过固定时间过期                .expireAfterWrite(2, TimeUnit.SECONDS)                // 初始的缓存空间大小                .initialCapacity(100)                // 缓存的最大条数                .maximumSize(1000)                .build();    }

3.编写一个缓存加入缓存,查询缓存的工具类
@Component@Slf4jpublic class CacheUtils {

    @Resource    Cache<String, Object> caffeineCache;

    public static final String CACHE_PREFIX = "staff_center";

    /**     * 查询缓存是否存在     *     * @param key     * @return     */    public boolean checkCacheByKey(Object key) {        String realKey = CACHE_PREFIX   "_"   key;        log.info("检查缓存是否存在key为======={}", realKey);        if (Objects.nonNull(caffeineCache.asMap().get(realKey))) {            log.info("缓存存在,执行缓存key为======={}", realKey);            return true;        } else {            log.info("缓存不存在,执行持久层,传入的key为======={}", realKey);            return false;        }    }

    /**     * 加入缓存     *     * @param key     * @return     */    public void addCache(Object key, CrispsResponse value) {        String realKey = CACHE_PREFIX   "_"   key;        log.info("添加缓存,缓存key可以为======={},value为========={}", realKey, value.getData());        caffeineCache.put(realKey, value);    }

    /**     * 查询缓存     *     * @param key     * @return     */    public Object getCache(Object key) {        String realKey = CACHE_PREFIX   "_"   key;        log.info("执行缓存,缓存key为======={}", realKey);        return caffeineCache.asMap().get(realKey);    }}
4.自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface LocalCache {    @AliasFor("cacheNames")    String[] value() default {};

    @AliasFor("value")    String[] cacheNames() default {};

    String key() default "";

    @Deprecated    String keyGenerator() default "";

}

5.编写aop
@Aspectpublic class CacheAspect {    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final String CACHE_KEY_ERROR_MESSAGE = "缓存Key %s 不能为NULL";    private static final String CACHE_NAME_ERROR_MESSAGE = "缓存名称不能为NULL";

    private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();

    @Autowired(required = false)    private KeyGenerator keyGenerator = new SimpleKeyGenerator();

  // 注入缓存工具类    @Resource    CacheUtils cacheUtils;

    @Pointcut("@annotation(net.crisps.cloud.common.annotation.LocalCache)")    public void pointcut() {    }

    @Around(" pointcut()")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        CacheOperationInvoker aopAllianceInvoker = getCacheOperationInvoker(joinPoint);        // 获取method        Method method = this.getSpecificMethod(joinPoint);        // 获取注解        LocalCache cacheAble = AnnotationUtils.findAnnotation(method, LocalCache.class);

        try {            // 执行查询缓存方法            return executeCacheAble(aopAllianceInvoker, cacheAble, method, joinPoint.getArgs(), joinPoint.getTarget());        } catch (Exception e) {            logger.error("异常信息为=={}", e.getMessage());            return aopAllianceInvoker.invoke();        }    }

  // 返回 CacheOperationInvoker
private CacheOperationInvoker getCacheOperationInvoker(ProceedingJoinPoint joinPoint) {        return () -> {            try {                return joinPoint.proceed();            } catch (Throwable ex) {                throw new CacheOperationInvoker.ThrowableWrapper(ex);            }        };    }

    /**     * 获取Method     */    private Method getSpecificMethod(ProceedingJoinPoint pjp) {        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();        Method method = methodSignature.getMethod();        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(pjp.getTarget());        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);        return specificMethod;    }

    /**     * 执行Cacheable切面     */    private Object executeCacheAble(CacheOperationInvoker invoker, LocalCache cacheAble,                                    Method method, Object[] args, Object target) {

        // 解析SpEL表达式获取cacheName和key        Assert.notEmpty(cacheAble.cacheNames(), CACHE_NAME_ERROR_MESSAGE);        Object key = generateKey(cacheAble.key(), method, args, target);        Assert.notNull(key, String.format(CACHE_KEY_ERROR_MESSAGE, cacheAble.key()));

        // 判断是否有缓存,没有则执行方法,加入缓存        if (cacheUtils.checkCacheByKey(key)) {            return cacheUtils.getCache(key);        } else {            // 调用本身方法,获取返回值            CrispsResponse crispsResponse = (CrispsResponse) invoker.invoke();            if (ResponseCode.SUCCESS.getCode() == crispsResponse.getCode() && Objects.nonNull(crispsResponse.getData())) {                cacheUtils.addCache(key, crispsResponse);            }            return crispsResponse;        }    }

    /**     * 解析SpEL表达式,获取注解上的key属性值     */    private Object generateKey(String keySpEl, Method method, Object[] args, Object target) {

        // 获取注解上的key属性值        Class<?> targetClass = getTargetClass(target);        if (StringUtils.hasText(keySpEl)) {            EvaluationContext evaluationContext = evaluator.createEvaluationContext(method, args, target, targetClass, CacheOperationExpressionEvaluator.NO_RESULT);            AnnotatedElementKey methodCacheKey = new AnnotatedElementKey(method, targetClass);            // 兼容传null值得情况            Object keyValue = evaluator.key(keySpEl, methodCacheKey, evaluationContext);            return Objects.isNull(keyValue) ? "null" : keyValue;        }        return this.keyGenerator.generate(target, method, args);    }}
6.配置aop
@Configuration@EnableAspectJAutoProxypublic class AopConfig {    @Bean    public CacheAspect getCacheAspect() {        return new CacheAspect();    }}
7.解析SpEL表达式 需要的类
package net.crisps.cloud.common.cache.exception;import org.springframework.context.expression.MethodBasedEvaluationContext;import org.springframework.core.ParameterNameDiscoverer;

import java.lang.reflect.Method;import java.util.HashSet;import java.util.Set;

class CacheEvaluationContext extends MethodBasedEvaluationContext {

   private final Set<String> unavailableVariables = new HashSet<String>(1);

   CacheEvaluationContext(Object rootObject, Method method, Object[] arguments, ParameterNameDiscoverer parameterNameDiscoverer) {      super(rootObject, method, arguments, parameterNameDiscoverer);   }

   public void addUnavailableVariable(String name) {      this.unavailableVariables.add(name);   }

   @Override   public Object lookupVariable(String name) {      if (this.unavailableVariables.contains(name)) {         throw new VariableNotAvailableException(name);      }      return super.lookupVariable(name);   }

}
package net.crisps.cloud.common.cache.exception;

import org.springframework.util.Assert;

import java.lang.reflect.Method;

class CacheExpressionRootObject {

    private final Method method;

    private final Object[] args;

    private final Object target;

    private final Class<?> targetClass;

    public CacheExpressionRootObject(Method method, Object[] args, Object target, Class<?> targetClass) {

        Assert.notNull(method, "Method必传");        Assert.notNull(targetClass, "targetClass必传");        this.method = method;        this.target = target;        this.targetClass = targetClass;        this.args = args;    }

    public Method getMethod() {        return this.method;    }

    public String getMethodName() {        return this.method.getName();    }

    public Object[] getArgs() {        return this.args;    }

    public Object getTarget() {        return this.target;    }

    public Class<?> getTargetClass() {        return this.targetClass;    }

}
package net.crisps.cloud.common.cache.exception;

import org.springframework.aop.support.AopUtils;import org.springframework.context.expression.AnnotatedElementKey;import org.springframework.context.expression.CachedExpressionEvaluator;import org.springframework.expression.EvaluationContext;import org.springframework.expression.Expression;

import java.lang.reflect.Method;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;

public class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {

    public static final Object NO_RESULT = new Object();

    public static final Object RESULT_UNAVAILABLE = new Object();

    public static final String RESULT_VARIABLE = "result";

    private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);

    private final Map<ExpressionKey, Expression> cacheNameCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);

    private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);

    private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);

    private final Map<AnnotatedElementKey, Method> targetMethodCache =            new ConcurrentHashMap<AnnotatedElementKey, Method>(64);    public EvaluationContext createEvaluationContext(Method method, Object[] args, Object target, Class<?> targetClass) {

        return createEvaluationContext(method, args, target, targetClass, NO_RESULT);    }

    public EvaluationContext createEvaluationContext(Method method, Object[] args,                                                     Object target, Class<?> targetClass, Object result) {

        CacheExpressionRootObject rootObject = new CacheExpressionRootObject(                method, args, target, targetClass);        Method targetMethod = getTargetMethod(targetClass, method);        CacheEvaluationContext evaluationContext = new CacheEvaluationContext(                rootObject, targetMethod, args, getParameterNameDiscoverer());        if (result == RESULT_UNAVAILABLE) {            evaluationContext.addUnavailableVariable(RESULT_VARIABLE);        } else if (result != NO_RESULT) {            evaluationContext.setVariable(RESULT_VARIABLE, result);        }        return evaluationContext;    }

    public Object key(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {

        return getExpression(this.keyCache, methodKey, expression).getValue(evalContext);    }

    public Object cacheName(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {

        return getExpression(this.cacheNameCache, methodKey, expression).getValue(evalContext);    }

    public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {        return getExpression(this.conditionCache, methodKey, conditionExpression).getValue(evalContext, boolean.class);    }

    public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {        return getExpression(this.unlessCache, methodKey, unlessExpression).getValue(evalContext, boolean.class);    }

    void clear() {        this.keyCache.clear();        this.conditionCache.clear();        this.unlessCache.clear();        this.targetMethodCache.clear();    }

    private Method getTargetMethod(Class<?> targetClass, Method method) {        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);        Method targetMethod = this.targetMethodCache.get(methodKey);        if (targetMethod == null) {            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);            if (targetMethod == null) {                targetMethod = method;            }            this.targetMethodCache.put(methodKey, targetMethod);        }        return targetMethod;    }

}
package net.crisps.cloud.common.cache.exception;

import org.springframework.expression.EvaluationException;

@SuppressWarnings("serial")class VariableNotAvailableException extends EvaluationException {

   private final String name;

   public VariableNotAvailableException(String name) {      super("Variable '"   name   "' is not available");      this.name = name;   }

   public String getName() {      return this.name;   }}
8. 加上自定义注解测试缓存

8.执行看控制台输出

来源:https://www.icode9.com/content-4-775151.html

(0)

相关推荐

  • Spring-AOP核心

    前置-代理设计 我们先介绍相关的几种代理设计方法 1.静态代理 // 原始业务接口 public interface UserService { void login(String username, ...

  • 小宋Java面经

    面试过程 Java后端实习/全职 自我介绍: 项目描述每一个自己实现的都必须了解,因为我很久没写代码,说的很不好,太菜了 网易和滴滴实习:多问基础技能,项目问的不多,我还能糊弄过去,基础技能面感觉还行 ...

  • java设计模式基础--拦截器

    由于动态代理一般比较难理解,一般都会设计一个拦截器接口供开发者使用,这样开发者就只用知道拦截器接口的方法,含义和作用即可,无须知道动态代理是怎么实现的. 以下代码用JDK动态代理来实现一个拦截器的逻辑 ...

  • Spring源码分析之AOP从解析到调用

    正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...

  • Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的 ...

  • 3.spring中aop的注解实现方式简单实例

    注解方式实现aop我们主要分为如下几个步骤(自己整理的,有更好的方法的话,欢迎交流codecjh@163.com): 1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类. 2. ...

  • Spring项目中自定义注解的使用

    Spring项目中自定义注解的使用

  • Spring AOP

    Spring AOP

  • 9,000+ 字,彻底征服 Spring AOP!

    基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹的是, 这些概念经过了中文翻译后, 变得面目全非, 相同的一个术语, 在不同的翻译下, ...

  • 还在用 Guava Cache?它才是 Java 本地缓存之王!

    作者:rickiyang 来源:https://www.cnblogs.com/rickiyang/p/11074158.html Guava Cache 的优点是封装了get,put操作:提供线程安 ...

  • Spring Boot自定义starter必知必会条件

    前言 在目前的Spring Boot框架中,不管是Spring Boot官方还是非官方,都提供了非常多的starter系列组件,助力开发者在企业应用中的开发,提升研发人员的工作效率,Spring Bo ...

  • 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

  • spring 框架常用注解

    spring 框架常用注解