zoukankan      html  css  js  c++  java
  • springBoot2.0+redis+fastJson+自定义注解实现方法上添加过期时间

    springBoot2.0集成redis实例

    一、首先引入项目依赖的maven jar包,主要包括 spring-boot-starter-data-redis包,这个再springBoot2.0之前好像是 spring-boot-starter-redis 这个,还有

    fastjson这个包,是用来序列化的。
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.44</version>-----主要版本
    </dependency>
    二、配置redis配置文件,以yml文件为例
    redis:
    database: 0
    # Redis服务器地址
    host: 10.18.104.13
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password:
    # 连接池最大连接数(使用负值表示没有限制)
    timeout: 1000 # 连接超时时间(毫秒)
    pool:
    maxTotal: 8
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    maxWaitMillis: -1
    # 连接池中的最大空闲连接
    maxIdle: 8
    # 连接池中的最小空闲连接
    testOnBorrow: true
    第三、编写 RedisConfig类,
    
    

    import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.cache.jcache.config.JCacheConfigurerSupport;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.cache.RedisCacheWriter;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import java.io.Serializable;
    import java.lang.reflect.Method;
    import java.time.Duration;
    import java.util.HashMap;
    import java.util.Map;

    import static org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig;

    @Configuration
    @Slf4j
    public class RedisConfig extends CachingConfigurerSupport {
    private final RedisConnectionFactory redisConnectionFactory;
    RedisConfig(RedisConnectionFactory redisConnectionFactory) {
    this.redisConnectionFactory = redisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    // set key serializer
    StringRedisSerializer serializer = TedisCacheManager.STRING_SERIALIZER;
    // 设置key序列化类,否则key前面会多了一些乱码
    template.setKeySerializer(serializer);
    template.setHashKeySerializer(serializer);

    // fastjson serializer
    GenericFastJsonRedisSerializer fastSerializer = TedisCacheManager.FASTJSON_SERIALIZER;
    template.setValueSerializer(fastSerializer);
    template.setHashValueSerializer(fastSerializer);
    // 如果 KeySerializer 或者 ValueSerializer 没有配置,则对应的 KeySerializer、ValueSerializer 才使用这个 Serializer
    template.setDefaultSerializer(fastSerializer);

    log.info("redis: {}", redisConnectionFactory);
    LettuceConnectionFactory factory = (LettuceConnectionFactory) redisConnectionFactory;
    log.info("spring.redis.database: {}", factory.getDatabase());
    log.info("spring.redis.host: {}", factory.getHostName());
    log.info("spring.redis.port: {}", factory.getPort());
    log.info("spring.redis.timeout: {}", factory.getTimeout());
    log.info("spring.redis.password: {}", factory.getPassword());

    // factory
    template.setConnectionFactory(redisConnectionFactory);
    template.afterPropertiesSet();
    return template;
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
    return (o, method, objects) -> {
    StringBuilder sb = new StringBuilder(32);
    sb.append(o.getClass().getSimpleName());
    sb.append(".");
    sb.append(method.getName());
    if (objects.length > 0) {
    sb.append("#");
    }
    String sp = "";
    for (Object object : objects) {
    sb.append(sp);
    if (object == null) {
    sb.append("NULL");
    } else {
    sb.append(object.toString());
    }
    sp = ".";
    }
    return sb.toString();
    };
    }

    /**
    * 配置 RedisCacheManager,使用 cache 注解管理 redis 缓存
    */
    @Bean
    @Override
    public CacheManager cacheManager() {
    // 初始化一个RedisCacheWriter
    RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

    // 设置默认过期时间:30 分钟
    RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofMinutes(30))
    // .disableCachingNullValues()
    // 使用注解时的序列化、反序列化
    .serializeKeysWith(TedisCacheManager.STRING_PAIR)
    .serializeValuesWith(TedisCacheManager.FASTJSON_PAIR);

    // Map<String, RedisCacheConfiguration> caches = new HashMap<>();
    // // 缓存配置
    // RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    // .entryTtl(Duration.ofSeconds(60))
    // .disableCachingNullValues()
    // // .prefixKeysWith("redis.service")
    // .serializeKeysWith(stringPair)
    // .serializeValuesWith(fastJsonPair);
    // caches.put("redis.service", config);
    // return new TedisCacheManager(cacheWriter, defaultCacheConfig, caches);

    return new TedisCacheManager(cacheWriter, defaultCacheConfig);
    }
    第五、配置 TedisCacheManager类,实现对 RedisCacheManager管理
    import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.cache.Cache;
    import org.springframework.cache.annotation.CacheConfig;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.cache.annotation.Caching;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.data.redis.cache.RedisCache;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.cache.RedisCacheWriter;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.util.ReflectionUtils;

    import java.time.Duration;
    import java.util.Collection;
    import java.util.LinkedHashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.Callable;

    /**
    * Redis 容易出现缓存问题(超时、Redis 宕机等),当使用 spring cache 的注释 Cacheable、Cacheput 等处理缓存问题时,
    * 我们无法使用 try catch 处理出现的异常,所以最后导致结果是整个服务报错无法正常工作。
    * 通过自定义 TedisCacheManager 并继承 RedisCacheManager 来处理异常可以解决这个问题。
    */
    @Slf4j
    public class TedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean {
    private ApplicationContext applicationContext;

    private Map<String, RedisCacheConfiguration> initialCacheConfiguration = new LinkedHashMap<>();

    /**
    * key serializer
    */
    public static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();

    /**
    * value serializer
    * <pre>
    * 使用 FastJsonRedisSerializer 会报错:java.lang.ClassCastException
    * FastJsonRedisSerializer<Object> fastSerializer = new FastJsonRedisSerializer<>(Object.class);
    * </pre>
    */

    public static final GenericFastJsonRedisSerializer FASTJSON_SERIALIZER = new GenericFastJsonRedisSerializer();

    /**
    * key serializer pair
    */
    public static final RedisSerializationContext.SerializationPair<String> STRING_PAIR = RedisSerializationContext
    .SerializationPair.fromSerializer(STRING_SERIALIZER);
    /**
    * value serializer pair
    */
    public static final RedisSerializationContext.SerializationPair<Object> FASTJSON_PAIR = RedisSerializationContext
    .SerializationPair.fromSerializer(FASTJSON_SERIALIZER);

    public TedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
    super(cacheWriter, defaultCacheConfiguration);
    }

    // public TedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
    // Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
    // super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
    // }

    @Override
    public Cache getCache(String name) {
    Cache cache = super.getCache(name);
    return new RedisCacheWrapper(cache);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
    String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
    for (String beanName : beanNames) {
    final Class clazz = applicationContext.getType(beanName);
    add(clazz);
    }
    super.afterPropertiesSet();
    }

    @Override
    protected Collection<RedisCache> loadCaches() {
    List<RedisCache> caches = new LinkedList<>();
    for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
    caches.add(super.createRedisCache(entry.getKey(), entry.getValue()));
    }
    return caches;
    }

    private void add(final Class clazz) {
    ReflectionUtils.doWithMethods(clazz, method -> {
    ReflectionUtils.makeAccessible(method);
    CacheExpire cacheExpire = AnnotationUtils.findAnnotation(method, CacheExpire.class);
    if (cacheExpire == null) {
    return;
    }
    Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
    if (cacheable != null) {
    add(cacheable.cacheNames(), cacheExpire);
    return;
    }
    Caching caching = AnnotationUtils.findAnnotation(method, Caching.class);
    if (caching != null) {
    Cacheable[] cs = caching.cacheable();
    if (cs.length > 0) {
    for (Cacheable c : cs) {
    if (cacheExpire != null && c != null) {
    add(c.cacheNames(), cacheExpire);
    }
    }
    }
    } else {
    CacheConfig cacheConfig = AnnotationUtils.findAnnotation(clazz, CacheConfig.class);
    if (cacheConfig != null) {
    add(cacheConfig.cacheNames(), cacheExpire);
    }
    }
    }, method -> null != AnnotationUtils.findAnnotation(method, CacheExpire.class));
    }

    private void add(String[] cacheNames, CacheExpire cacheExpire) {
    for (String cacheName : cacheNames) {
    if (cacheName == null || "".equals(cacheName.trim())) {
    continue;
    }
    long expire = cacheExpire.expire();
    log.info("cacheName: {}, expire: {}", cacheName, expire);
    if (expire >= 0) {
    // 缓存配置
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofSeconds(expire))
    .disableCachingNullValues()
    // .prefixKeysWith(cacheName)
    .serializeKeysWith(STRING_PAIR)
    .serializeValuesWith(FASTJSON_PAIR);
    initialCacheConfiguration.put(cacheName, config);
    } else {
    log.warn("{} use default expiration.", cacheName);
    }
    }
    }

    protected static class RedisCacheWrapper implements Cache {
    private final Cache cache;

    RedisCacheWrapper(Cache cache) {
    this.cache = cache;
    }

    @Override
    public String getName() {
    // log.info("name: {}", cache.getName());
    try {
    return cache.getName();
    } catch (Exception e) {
    log.error("getName ---> errmsg: {}", e.getMessage(), e);
    return null;
    }
    }

    @Override
    public Object getNativeCache() {
    // log.info("nativeCache: {}", cache.getNativeCache());
    try {
    return cache.getNativeCache();
    } catch (Exception e) {
    log.error("getNativeCache ---> errmsg: {}", e.getMessage(), e);
    return null;
    }
    }

    @Override
    public ValueWrapper get(Object o) {
    // log.info("get ---> o: {}", o);
    try {
    return cache.get(o);
    } catch (Exception e) {
    log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
    return null;
    }
    }

    @Override
    public <T> T get(Object o, Class<T> aClass) {
    // log.info("get ---> o: {}, clazz: {}", o, aClass);
    try {
    return cache.get(o, aClass);
    } catch (Exception e) {
    log.error("get ---> o: {}, clazz: {}, errmsg: {}", o, aClass, e.getMessage(), e);
    return null;
    }
    }

    @Override
    public <T> T get(Object o, Callable<T> callable) {
    // log.info("get ---> o: {}", o);
    try {
    return cache.get(o, callable);
    } catch (Exception e) {
    log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
    return null;
    }
    }

    @Override
    public void put(Object o, Object o1) {
    // log.info("put ---> o: {}, o1: {}", o, o1);
    try {
    cache.put(o, o1);
    } catch (Exception e) {
    log.error("put ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
    }
    }

    @Override
    public ValueWrapper putIfAbsent(Object o, Object o1) {
    // log.info("putIfAbsent ---> o: {}, o1: {}", o, o1);
    try {
    return cache.putIfAbsent(o, o1);
    } catch (Exception e) {
    log.error("putIfAbsent ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
    return null;
    }
    }

    @Override
    public void evict(Object o) {
    // log.info("evict ---> o: {}", o);
    try {
    cache.evict(o);
    } catch (Exception e) {
    log.error("evict ---> o: {}, errmsg: {}", o, e.getMessage(), e);
    }
    }

    @Override
    public void clear() {
    // log.info("clear");
    try {
    cache.clear();
    } catch (Exception e) {
    log.error("clear ---> errmsg: {}", e.getMessage(), e);
    }
    }
    }
    六、自定义注解,可以在缓存方法上添加过期时间。

    import org.springframework.core.annotation.AliasFor;

    import java.lang.annotation.*;

    /**
    * @author wjw
    * 2018/7/12
    */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface CacheExpire {
    /**
    * expire time, default 60s
    */
    @AliasFor("expire")
    long value() default 60L;

    /**
    * expire time, default 60s
    */
    @AliasFor("value")
    long expire() default 60L;

    }
    七、案例使用:

    /**
    * 利用jdbcTemplate进行查询
    * @param userID
    * @return
    */

    @Cacheable(value = "user",key = "'user_'.concat(#root.args[0])",sync=true)
    @CacheExpire(expire = 60)
    public List<Map<String,Object>> getUser(Long userID){
    StringBuilder sql = new StringBuilder("");
    System.out.print(122);
    sql.append("select * from tbl_sys_user t where t.user_id = ? ");
    return jdbcTemplate.queryForList(sql.toString(),userID);
    }
    八,最后在启动入口类上添加注解
    @EnableCaching






  • 相关阅读:
    JavaScript + Table 系列:排序
    asp.net 2.0中傻瓜式使用soap header
    Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别
    asp.net 2.0 导出DataTable到Excel中
    ASP.NET实用技巧(一)
    【ASP.NET】基础补习之FileUpload
    ASP.NET AJAX入门系列(8):自定义异常处理
    Linq To Sql进阶系列(五)Store Procedure篇
    Linq To Sql进阶系列(六)用object的动态查询与保存log篇
    深究“通过样式表实现固定表头和列”
  • 原文地址:https://www.cnblogs.com/wjwen/p/9301119.html
Copyright © 2011-2022 走看看