缓存

在接口服务中,有必要对于接口进行缓存处理,尤其是GET请求,如果每个接口单独添加的话会存在很多的重复的逻辑,所以可以编写一套通用的解决方案。

实现思路:

  • 通过拦截器实现对请求的拦截,在拦截器中实现缓存的命中。
  • 通过ResponseBodyAdvice进行对响应的拦截,可以将数据缓存到Redis中。
  • 考虑到,不能对于所有的请求都一刀切,所以需要创建@Cache注解进行标记,只有标记的Controller才进行缓存处理。
  • 缓存的处理中,仅针对GET请求处理,其他的请求均不做处理。

1.自定义注解

import java.lang.annotation.*;

/**
 * 被标记为Cache的Controller进行缓存,其他情况不进行缓存
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented //标记注解
public @interface Cache {

    /**
     * 缓存时间,默认为60秒
     * @return
     */
    String time() default "60";
}

2.采用拦截器进行缓存命中

编写拦截器:RedisCacheInterceptor。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.*.server.utils.Cache;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RedisCacheInterceptor implements HandlerInterceptor {

	//注入enable
	@Value("${*.cache.enable}")
    private Boolean enable;
	
	//注入Redis
	@Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    //
 	private static final ObjectMapper MAPPER = new ObjectMapper()

	@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//缓存的全局开关的校验,如果没有开启,直接return true,不用做任何处理,直接放行
        if (!enable) {
            return true;
        }

        //校验handler是否是HandlerMethod,如果是,说明匹配到controller里的具体方法,如果不是,不用处理,return true放行,
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

		
		//将handle强转为HandlerMethod,查询他是不是有个注解是GetMapping,如果不包含,直接返回
        //判断是否为get请求
        if (!((HandlerMethod) handler).hasMethodAnnotation(GetMapping.class)) {
            return true;
        }

        //判断是否添加了@Cache注解
        if (!((HandlerMethod) handler).hasMethodAnnotation(Cache.class)) {
            return true;
        }

        //redis 的key
        String redisKey = createRedisKey(request);
        //redis 缓存
        String cacheData = this.redisTemplate.opsForValue().get(redisKey);
        //如果为空,缓存未命中
        if(StringUtils.isEmpty(cacheData)){
            //缓存未命中
            return true;
        }
        //缓存命中
        // 将data数据进行响应,将缓存返回
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(cacheData);

        return false;
        }

    /**
     * 生成redis中的key,规则:SERVER_CACHE_DATA_MD5(url + param + token)
     *根据请求参数request生成
     * @param request
     * @return
     */
    public static String createRedisKey(HttpServletRequest request) throws Exception {
        String url = request.getRequestURI();
        //给字符串序列化为一个json
        String param = MAPPER.writeValueAsString(request.getParameterMap());
        String token = request.getHeader("Authorization");

        String data = url + "_" + param + "_" + token;
        //返回redis key  
        //MD5加密
        return "SERVER_CACHE_DATA_" + DigestUtils.md5Hex(data);
    }
}
}

application.properties:

#是否开启缓存
project.cache.enable=false
#Redis相关配置
spring.redis.jedis.pool.max-wait = 5000ms
spring.redis.jedis.pool.max-Idle = 100
spring.redis.jedis.pool.min-Idle = 10
spring.redis.timeout = 10s
spring.redis.cluster.nodes = 192.168.31.81:6379,192.168.31.81:6380,192.168.31.81:6381
spring.redis.cluster.max-redirects=5

注册拦截器到Spring容器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
	//注入拦截器
    @Autowired
    private RedisCacheInterceptor redisCacheInterceptor;

	//拦截规则("/**"),所有的请求都会被我们拦截
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.redisCacheInterceptor).addPathPatterns("/**");
    }
}

3.响应结果写入到缓存

使用ResponseBodyAdvice进行对响应结果处理,将结果写入到Redis中:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.*.server.utils.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.concurrent.TimeUnit;

//实现ResponseBodyAdvice接口
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {

    @Value("${*.cache.enable}")
    private Boolean enable;

	//注入redis
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final ObjectMapper MAPPER = new ObjectMapper();

	//判断它是否支持这个操作
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //  三个条件 开关处于开启状态  是get请求  包含了@Cache注解
        //	只有三个条件同时满足,才做处理,执行下面的方法
        return enable && returnType.hasMethodAnnotation(GetMapping.class)
                && returnType.hasMethodAnnotation(Cache.class);
    }

	//条件都满足,执行此方法,把数据写入缓存中 ,这里的request是 ServerHttpRequest,拦截器里是HttpServletRequest,所以下面获取时需要转换
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
         //如果空,不处理 ,返回null               
        if (null == body) {
            return null;
        }

		// 需要注入redis
        try {

            String redisValue = null;
            if (body instanceof String) {
                redisValue = (String) body;
            } else {
                redisValue = MAPPER.writeValueAsString(body);
            }
            //强转request
            String redisKey = RedisCacheInterceptor.createRedisKey(((ServletServerHttpRequest) request).getServletRequest());

            Cache cache = returnType.getMethodAnnotation(Cache.class);

            //缓存的时间单位是秒
            this.redisTemplate.opsForValue().set(redisKey, redisValue, Long.valueOf(cache.time()), TimeUnit.SECONDS);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return body;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值