Spring cache implementa un tiempo de vencimiento personalizado de redis

El artículo anterior ( la configuración de la caché de Spring especifica el tiempo de caducidad de la clave ) ha mencionado cómo implementar el tiempo de caducidad personalizado de la caché de primavera, pero después de usarlo, no es lo suficientemente elegante como para quejarse (también borracho), y sí, como una persecución A el programador que (no) busca (convencido) mentalidad debería hacerlo mejor.
Después de la reorganización, la idea general es la siguiente:

  1. Personalice una anotación @TimeToLive, incluido el tiempo de vencimiento y la unidad de tiempo
  2. Agregue una anotación personalizada @TimeToLive al método
  3. Defina un aspecto, utilizando el principio de primavera AOP, pointcut contiene este método de anotación personalizado
  4. Obtenga la relación de mapeo entre cacheName y Method a través del aspecto AOP, y almacénelo localmente
  5. Personalice el personalizador RedisCacheManager para anular el método computeExpiration de RedisCacheManager

La siguiente es la parte del código:

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @Description Redis过期时间注解
 * @Author huangd
 * @Date 2020-01-02
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeToLive {

    long ttl() default 60 * 60;

    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class VehicleSeriesCacheService {

    @Autowired
    private XXXMapper xxxMapper;

    @Cacheable(value = "ota.vehicle.series", key = "#id + ''")
    **@TimeToLive(ttl = 20, timeUnit = TimeUnit.MINUTES)**
    public VehicleSeries fetchById(Long id) {
        return xxxMapper.fetch(id);
    }
}
import com.mng.boot.common.annotation.TimeToLive;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

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

/**
 * @Description Spring cache 过期时间AOP处理
 * @Author huangd
 * @Date 2020-01-02
 **/
@Aspect
@Component
public class CacheTtlAdvice implements Ordered {
    private HashMap<String, Method> cacheNameMethodCache = new HashMap<>(16);

    /**
     * 初始化过期时间配置
     * @param joinPoint 切入点
     */
    @Before("@annotation(com.xiaopeng.ota.mng.boot.common.annotation.TimeToLive)")
    public void before(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        TimeToLive timeToLive = AnnotationUtils.findAnnotation(method, TimeToLive.class);
        Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);

        if(null != cacheable && cacheable.cacheNames().length > 0){
            String[] cacheNames = cacheable.cacheNames();
            if(timeToLive != null){
                for(String cacheName : cacheNames){
                    if(!cacheNameMethodCache.containsKey(cacheName)){
                        cacheNameMethodCache.put(cacheName, method);
                    }
                }
            }
        }
    }

    @Override
    public int getOrder() {
        return 1000;
    }
    
    public HashMap<String, Method> getCacheNameMethodCache() {
        return cacheNameMethodCache;
    }
}

@Slf4j
@ConditionalOnProperty(name = "spring.cache.type", havingValue = "redis")
@Service
public class CustomizerRedisCacheManager extends RedisCacheManager {

    @Autowired
    private CacheTtlAdvice cacheTtlAdvice;

    public CustomizerRedisCacheManager(@Qualifier("redisTemplate") RedisOperations redisOperations) {
        super(redisOperations);
    }

    @Override
    public long computeExpiration(String name) {
        long defaultExpiration = super.computeExpiration(name);
        Method method = cacheTtlAdvice.getCacheNameMethodCache().get(name);
        if (method != null) {
            TimeToLive timeToLive = AnnotationUtils.findAnnotation(method, TimeToLive.class);
            return timeToLive.timeUnit().toSeconds(timeToLive.ttl());
        }

        return defaultExpiration;
    }
}

Configure la clave de RedisTemplate como modo de serialización StringRedisSerializer (serialización JDK predeterminada)

@Configuration
public class CustomCachingConfigurerSupport extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

Nota: La clase de aspecto CacheTtlAdvice debe implementar la interfaz Ordered e implementar el método getOrder, porque @Cacheable también se implementa a través de AOP, lo que implicará problemas de prioridad. Cuanto menor sea el valor de getOrder, la primera ejecución. El valor predeterminado es (2147483647). puede ver el orden de @EnableCaching El valor predeterminado, donde nuestras anotaciones personalizadas deben ejecutarse primero.

Supongo que te gusta

Origin blog.csdn.net/huangdi1309/article/details/103831716
Recomendado
Clasificación