posts

Spring boot自定义注解实现接口缓存

结合 Spring AOP 与 Redis,通过自定义注解优雅地为接口加上缓存以提升访问效率。

结合spring boot aop切片编程的特性,采取自定义注解的形式,借助缓存redis实现接口缓存,提高稳定接口访问效率

自定义注解

/**
 * 接口缓存自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiCache {
    // 缓存时间,默认缓存一分钟
    long validityPeriod() default 60;
}

定义切入点

package cn.hsa.neu.dqs.common.aop;


import cn.hsa.hsaf.core.framework.util.DateUtil;
import cn.hsa.neu.dqs.common.utils.CacheUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class ApiCacheAspect {

	@Autowired
	CacheUtil cacheUtil;


	@Around("@annotation(cn.hsa.neu.dqs.common.aop.ApiCache)")
	public Object action(ProceedingJoinPoint joinPoint) throws Throwable {

		String bizDate = DateUtil.getCurrentDate_String(DateUtil.DATEFORMAT);

		Signature signature = joinPoint.getSignature();

		// 反射获取目标类名
		String className = joinPoint.getTarget().getClass().getName();
		// 获取目标方法的名称
		String methodName = signature.getName();
		// 获取方法传入参数
		String params = Arrays.toString(joinPoint.getArgs());
		// 合成键值
		String key = className+":"+methodName+":"+bizDate+":"+params;
		// 被切的方法 返回类型
		Class<?> methodReturnType = Object.class;
		if (signature instanceof MethodSignature) {
			MethodSignature methodSignature = (MethodSignature) signature;
			// 被切的方法
			Method method = methodSignature.getMethod();
			// 返回类型
			methodReturnType = method.getReturnType();
		}

		// 获取注解的方法
		Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
		ApiCache annotation = method.getAnnotation(ApiCache.class);
		// 获取缓存有效期
		 long validityPeriod = annotation.validityPeriod();
		// 获取缓存的接口返回值
		Object response = CacheUtil.getStr(key);
		if(response == null){
			// 如果没有缓存接口返回值则执行原方法
			log.info(key+":cache get!");
			response = joinPoint.proceed();
			// 保存缓存
			CacheUtil.putStrWithTimeout(key, JSON.toJSONString(response),validityPeriod);
		}else{
			// 否则直接从缓存中获取接口返回值
			response=JSONObject.parseObject(response.toString(), methodReturnType);
		}
		return response;
	}
}

示例

使用时直接在需要配置接口缓存的接口controller/service上加上刚才定义的自定义注解即可。(可通过参数validityPeriod配置缓存有效时间)

@ApiCache(validityPeriod = 60*60*12)// 接口缓存12小时
@PostMapping("/test")
public Object test(Object input) throws Exception {
    ...
}