springboot整合redis Template和注解 配置失效时间 延迟队列

创建一个基础的springboot项目,这个我就不多说了,不明白的去搜教程。

pom文件

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>
 </dependencies>

application.properties

spring.profiles.active=test

application-test.yml

server:
  port: 8899
mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
#打印sql,不打印结果集(结果集多会造成性能影响)
logging:
  level:
    com.example.demo.mapper: debug
spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    # 正式服务
    host: 127.0.0.1
    #    password: 123456
    password: 123456
    # 腾讯云
    #    host: 62.234.77.34
    # Redis服务器连接端口
    port: 6379
    # Redis超时时间
    timeout: 5000
    jedis:
      pool:
        #最大连接数据库连接数,设 0 为没有限制
        max-active: 8
        #最大等待连接中的数量,设 0 为没有限制
        max-idle: 8
        #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
        max-wait: -1ms
        #最小等待连接中的数量,设 0 为没有限制
        min-idle: 0
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          max-wait: -1ms
          min-idle: 0
        shutdown-timeout: 100ms
  datasource:
      driverClassName: com.mysql.jdbc.Driver
      username: root
      password: 123456
      # spring2.0此处为jdbc-url
      url: jdbc:mysql://localhost:3306/yfDemo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
      type: com.alibaba.druid.pool.DruidDataSource

配置Druid的监控,可以在地址:http://127.0.0.1:8899/druid/login.html,那么这里我配置的端口是8899,所有地址后面是项目的端口不是8080

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfiguration {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return  new DruidDataSource();
    }

    //配置Druid的监控
    //1、配置一个管理后台的Servlet
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String,String> initParams = new HashMap<>();
        initParams.put("loginUsername","admin");
        initParams.put("loginPassword","123456");
        initParams.put("allow","");//默认就是允许所有访问
        //initParams.put("deny","127.0.0.1");
        bean.setInitParameters(initParams);
        return bean;
    }


    //2、配置一个web监控的filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        Map<String,String> initParams = new HashMap<>();
        initParams.put("exclusions","*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }
}

redis配置,这里是通过集合的key判断这个数据存储时长,可实现动态设置失效时间。

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.google.common.collect.ImmutableMap;
import org.springframework.cache.CacheManager;
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.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Map;

@Configuration
public class RedisCacheManage {
   @Bean
   public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
       RedisTemplate<String, Object> template = new RedisTemplate();
       template.setConnectionFactory(factory);
       StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
       //配置使用FastJson进行序列化和反序列化操作
       FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
       template.setKeySerializer(stringRedisSerializer);
       template.setHashKeySerializer(stringRedisSerializer);
       template.setValueSerializer(fastJsonRedisSerializer);
       template.setHashValueSerializer(fastJsonRedisSerializer);
       template.afterPropertiesSet();
       //开启Redis事务
       /**
        * 为何要开启事务呢?redis本身是单线程执行的one by one
        * 虽然都是原子性操作,那需要多个原子性的操作才算一个完整的事件呢
        * 譬如 redis get set可能就是一对操作才能保证操作的完整性
        * 譬如记录访问数 value 从0开始,在并发情况下多线程会同时查询到 value 为0 的情况,
        * 此时两线程都得到value 0 为此都 +1操作 执行结果 value=1,而我们期望的结果至少是2才对
        * 所以需要用事务来解决这一堆操作(get set)
        */
       template.setEnableTransactionSupport(true);
       return template;
   }

    @Bean
    public CacheManager cacheManager(RedisTemplate<String, Object> template) {

        //配置多个缓存名称 这里我们用个能用的模板  这里有坑点往下看
        RedisCacheConfiguration templateRedisCacheCfg = RedisCacheConfiguration
                .defaultCacheConfig()
                // 设置key为String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
                // 设置value 为自动转Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
                // 不缓存null
                .disableCachingNullValues();
        //这里对RedisCacheConfiguration不熟悉的可能会有疑问,我们这样批量templateRedisCacheCfg.entryTtl(Duration.ofMinutes(15))最后不都是同一个引用吗,过期时间会生效吗?
        Map<String, RedisCacheConfiguration> expires = ImmutableMap.<String, RedisCacheConfiguration>builder()
                .put("1m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(1)))
                .put("30m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(30)))
                .put("60m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(60)))
                .put("1d", templateRedisCacheCfg.entryTtl(Duration.ofDays(1)))
                .put("30d", templateRedisCacheCfg.entryTtl(Duration.ofDays(30)))
                .put("common-30d", templateRedisCacheCfg.entryTtl(Duration.ofDays(30)))
                .build();

        RedisCacheManager redisCacheManager =
                RedisCacheManager.RedisCacheManagerBuilder
                        // Redis 连接工厂
                        .fromConnectionFactory(template.getConnectionFactory())
                        // 默认缓存配置
                        .cacheDefaults(templateRedisCacheCfg.entryTtl(Duration.ofHours(1)))
                        // 配置同步修改或删除 put/evict
                        .transactionAware()
                        .initialCacheNames(expires.keySet())
                        .withInitialCacheConfigurations(expires)
                        .build();
        return redisCacheManager;
    }
}

这个时候有一个关键的注解,必须要加,不然redis不起作用,MapperScan注解是扫描mapper接口的。

@MapperScan("com.example.demo.mapper")
@EnableCaching

然后我们写一个接口查询的接口,mybatis我就不细讲了,

@RequestMapping("/redis")
@RestController
/**
 * redis类型
 * String List Hash Set Zset
 */
public class RedisController {

    @Autowired
    private RedisService redisService;

    @GetMapping("/getCompany")
    public List<CompanyModel> getCompany(){
       return  redisService.getCompany();
    }
}
@Service
public interface RedisService {

    List<CompanyModel> getCompany();
}
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private CompanyMapper companyMapper;


    @Override
    @Cacheable(value = "1m",key = "12")
    public List<CompanyModel> getCompany() {
      return   companyMapper.selectCompanyAll();
    }
}

@Repository
public interface  CompanyMapper {
    List<CompanyModel> selectCompanyAll();
}
<select id="selectCompanyAll" resultType="com.example.demo.model.CompanyModel">
        SELECT ID id,CORP_NAME name FROM TC_COMPANY_INFO
    select>

redis Template 方式

其他重要的代码就省略了,看service

@Autowired
public RedisTemplate redisTemplate;
public void test1(){
        Coupon coupon = new Coupon();
        coupon.setGetTime("2022/1/21");
        coupon.setMaoney(20);
        coupon.setName("优惠卷");
        coupon.setStart(0);
        coupon.setTimeOut(1);
        coupon.setId(UUID.randomUUID().toString());
        couponMapper.inserCoupon(coupon);
        String json = JSONObject.toJSONString(coupon);
        redisTemplate.opsForValue().set(coupon.getId(),json,40,TimeUnit.SECONDS);
    }

在这里我在数据库里面插入了一条数据,然后又将这个数据又存到redis中

springboot整合redis Template和注解 配置失效时间 延迟队列_第1张图片

可以看到这种格式是有问题的,查询出来会乱码

重写RedisTemplate,防止乱码

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class redisconfig {
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

重写RedisTemplate这个我们再添加一次,就不会存在乱码,我们再看看

springboot整合redis Template和注解 配置失效时间 延迟队列_第2张图片

redis 延迟队列

就比如我们说的抢票之后不付款,30分钟之后自动把票放出来。比如外卖优惠卷是不是有一个使用时间,如果这个时间过了,我们是不是使用不了,那么也可以用延迟队列来实现。

配置监听

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class redisconfig {
    /**
     * TODO 监听 Redis键过期事件
     * @author zhuzhen
     * @date 14:38 2018/12/21
     * @param connectionFactory
     * @return org.springframework.data.redis.listener.RedisMessageListenerContainer
     */
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

@Component
public class RedisExpiredListener  extends KeyExpirationEventMessageListener {

    public RedisExpiredListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 用户做自己的业务处理即可,注意message.toString()可以获取失效的key
        System.out.println("message>>>  " + message);
        System.out.println("pattern>>>  " + new String(pattern));
        String expiredKey = message.toString();
        System.out.println("Redis的键:" + expiredKey);
        //具体的逻辑代码
    }
}

很简单,只要有失效的,就会被监听到,onMessage方法就会执行,那么我们是不是可以在这里做文章,只对key中存在什么才怎么怎么样。

链接:https://pan.baidu.com/s/1dsAVgAhp6GWSPchplmH6OA

提取码:yyds

这个是源码

你可能感兴趣的:(springboot,Mybatis,redis,spring,boot,java)