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.执行看控制台输出
赞 (0)