自定义注解实现Redis缓存
使用redis做缓存,很多情况下写的都是一样的模板代码,且代码侵入大,于是封装成注解,后面需要缓存的时候只需要加上注解就可以了,话不多说,都在代码里
注解定义:
CacheData
import java.lang.annotation.*; /** * 注解 CacheData 用于简便处理需要进行缓存的操作 * 注意 增加了全局缓存开关,参数为 global_cache_open_status_key, * 若是要关闭所有使用这个注解的缓存,可在nacos 配置中心或者其他配置文件配置 global_cache_open_status_key: false 即可关闭 * 若是要关闭单个使用注解缓存的地方,在nacos 配置中心或者其他配置文件的地方 配置对应 prefix值: false 即可 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CacheData { /** * 前缀 * @return */ String prefix(); /** * 后缀表达式,为空时候表示不需要后缀表达式,采用 SPEL * eg: * 1.不支持加固定后缀!!! 目前spEl数据源是来自方法入参,要是需要固定后缀,放置在前缀就好了; * 2.访问参数 比如 methodName(String userId, String pageId) ->"#userId+'_'+#pageId"; * 3.访问对象内属性 比如 methodName(UserBO userBO) ->"#userBO.name"; * 4.访问集合数据 比如 methodName(List<String> list) -> "#list.toString()"; * * @return */ String suffixExpression() default ""; /** * 缓存时间 秒 * @return */ long expireSecond() default 300; }
切面类:
import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.sd.outbound.common.annotation.CacheData; import com.sd.outbound.core.CoreConstants; import com.soudian.common.StringHelper; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.env.Environment; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.lang.reflect.Type; @Slf4j @Aspect @Component public class CacheDataAspect { private ExpressionParser parser = new SpelExpressionParser(); private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); @Autowired StringRedisTemplate stringRedisTemplate; @Autowired private Environment env; @Around("@annotation(cacheData)") public Object cacheDataAround(ProceedingJoinPoint pjp, CacheData cacheData) throws Throwable { String key = cacheData.prefix(); String currentCacheOpenStatus = env.getProperty(key); String globalCacheStatus = env.getProperty(CoreConstants.GLOBAL_CACHE_OPEN_STATUS_KEY); log.info("cacheOpenStatus of {} = {}, globalCacheStatus={}", key, currentCacheOpenStatus, globalCacheStatus); if(Boolean.FALSE.toString().toLowerCase().equals(globalCacheStatus) || Boolean.FALSE.toString().toLowerCase().equals(currentCacheOpenStatus)){ return pjp.proceed(); } long expireSecond = cacheData.expireSecond(); Method method = getMethod(pjp); Type genericReturnType = method.getGenericReturnType(); if(StringHelper.isNotEmpty(cacheData.suffixExpression())){ key = StrUtil.join(StrUtil.COLON, key, parseSpel(getMethod(pjp), pjp.getArgs(), cacheData.suffixExpression())); } String data = stringRedisTemplate.opsForValue().get(key); Object result = null; if(StringHelper.isNotEmpty(data)){ result = JSON.parseObject(data, genericReturnType); log.info("CacheData get data from cache in method: {}", method.getName()); }else { result = pjp.proceed(); stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(result), expireSecond); log.info("CacheData get data from db in method:{}", method.getName()); } return result; } private Object parseSpel(Method method, Object[] arguments, String spel) { String[] params = discoverer.getParameterNames(method); EvaluationContext context = new StandardEvaluationContext(); for (int len = 0; len < params.length; len++) { context.setVariable(params[len], arguments[len]); } try { Expression expression = parser.parseExpression(spel); return expression.getValue(context); } catch (Exception e) { return StrUtil.EMPTY; } } private static Method getMethod(ProceedingJoinPoint pjp) { MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); if (method.getDeclaringClass().isInterface()) { try { method = pjp .getTarget() .getClass() .getDeclaredMethod(pjp.getSignature().getName(), method.getParameterTypes()); } catch (SecurityException | NoSuchMethodException e) { throw new RuntimeException(e); } } return method; } }
使用:
@CacheData(prefix = "user_authority", suffixExpression = "#userId+'_'+#pageId") public List<String> getUserIdDataAuthority(String userId,Long pageId){ ...业务代码...
}
OK.