package com.iformall.aop; import com.alibaba.fastjson.JSONObject; import com.iformall.annotation.RedisCache; import com.iformall.common.ErrorCode; import com.iformall.domain.po.WxCUser; import com.iformall.domain.po.base.BaseCUserEntity; import com.iformall.exception.MallinkException; import com.iformall.service.CUserTokenService; import com.iformall.utils.HashUtil; import com.iformall.utils.RedisLock; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Objects; @Slf4j @Aspect @Component public class RedisCacheAspect { /** * 参数分隔符 param1|param2|param3 **/ private static final String DELIMITER_PARAMS = "|"; /** * key 分隔符 */ private static final String DELIMITER_KEY = ":"; @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired RedisLock redisLock; @Autowired private CUserTokenService cUserTokenService; @Pointcut("@annotation(com.iformall.annotation.RedisCache)") public void cacheAspect() { } @Around("cacheAspect()") public Object round(ProceedingJoinPoint jp) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 查询token信息 //从header中获取token String token = request.getHeader("token"); //如果header中不存在token,则从参数中获取token if (StringUtils.isBlank(token)) { token = request.getParameter("token"); } if(StringUtils.isBlank(token) || "null".equals(token) || "undefined".equals(token)){ token=""; //throw new MallinkException(ErrorCode.NET_TOKEN_EMPTY.getCode(),"token为空["+token+"]"); } log.info("token>>>>>>>>>>>>>>>"+token); String tenantId; BaseCUserEntity cUser = null ; if(StringUtils.isNotBlank(token)) { cUser = cUserTokenService.getByToken(token); } if (Objects.isNull(cUser) || cUser.getExpireTime().getTime() < System.currentTimeMillis()) { tenantId = "0"; } else { tenantId = cUser.getTenantId(); } // 请求参数 Object[] args = jp.getArgs(); // 接口 返回结果 Object result; String redisKey; //得到注释的名称 MethodSignature signature = (MethodSignature) jp.getSignature(); //判断tag是否在用户权限中 如果存在加入参数查询 Method method = signature.getMethod(); RedisCache cache = method.getAnnotation(RedisCache.class) ; if (cache.autoKey()) { // 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3) String requestUrl = request.getRequestURI(); // 接口路径 reqPath => /api/xxx String reqPath = requestUrl.substring(requestUrl.indexOf("/api/")); if (cache.md5()) { String md5Before = StringUtils.join(args, DELIMITER_PARAMS); redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, HashUtil.md5(StringUtils.join(token, md5Before, DELIMITER_PARAMS))); } else { redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, StringUtils.join(token,args, DELIMITER_PARAMS)); } } else { redisKey = cache.key(); } // redisKey 不存在,获取接口数据并返回 if (StringUtils.isBlank(redisKey)) { result = jp.proceed(args); } else { ValueOperations valueOperations = stringRedisTemplate.opsForValue(); String value = valueOperations.get(redisKey); if (value == null) { String lockKey = StringUtils.join(redisKey, ":", "lock"); long time = System.currentTimeMillis() + 2000; String timeStr = String.valueOf(time); try { //分布式锁,保证一个线程读DB,其它线程排队 if (redisLock.lock2(lockKey, timeStr)) { log.debug("CacheAspect 读库中, key:{}: " + lockKey); result = jp.proceed(args); String json_data = JSONObject.toJSONString(result); valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit()); log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime()); } else { log.debug("CacheAspect 读库等待中, key:{}: " + lockKey); Thread.sleep(2000); value = valueOperations.get(redisKey); result = parseCache(jp, redisKey, value); } } catch (Throwable throwable) { log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()), jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable); throw throwable; } finally { redisLock.unlock(lockKey, timeStr); } } else { result = parseCache(jp, redisKey, value); } // 计数器 stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1); } return result; } private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) { if (StringUtils.isBlank(value)) { log.debug("CacheAspect 缓存不存在,解析缓存数据为空:{},key:{}", value, redisKey); return null; } Class returnType = ((MethodSignature) jp.getSignature()).getReturnType(); log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey); return JSONObject.parseObject(value, returnType); } }