SpringBoot整合Redis笔记(Kotlin)

准备

项目依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

yaml配置

spring:
  redis:
    port: 6379
    host: localhost
    password:

StringRedisTemplate

该类在RedisAutoConfiguration已经自动注入IOC:

@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

我们直接注入即可

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {

    }

}

设置值

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {
        stringRedisTemplate
                .opsForValue().set("user", "kt")                                 //相当于原生redis的SET 不过期
        stringRedisTemplate
                .opsForValue().set("user", "kt", 1, TimeUnit.MINUTES)            //相当于原生redis的SET 加上过期时间
        stringRedisTemplate
                .opsForValue().setIfAbsent("user", "kt")                         //相当于原生redis的SETNX 不过期
        stringRedisTemplate
                .opsForValue().setIfAbsent("user", "kt", 1, TimeUnit.MINUTES)    //相当于原生redis的SETNX 加上过期时间
        stringRedisTemplate
                .opsForValue().setIfPresent("user", "kt")                        //如存在该key,则将k的value更新为kotlin 不过期
        stringRedisTemplate
                .opsForValue().setIfPresent("user", "kt", 1, TimeUnit.MINUTES)   //如存在该key,则将k的value更新为kotlin 加上过期时间
        stringRedisTemplate
                .opsForValue().getAndSet("user", "kt")                           //如存在该key,则返回旧的value值,然后更新该旧value更新	
        stringRedisTemplate															 
                .opsForValue()
                .multiSet(mapOf("1" to "2", "2" to "3", "3" to "4"))             //同时设置一个或多个 key-value 对
        
        stringRedisTemplate
                .opsForValue()
                .multiSetIfAbsent(
                    mapOf("1" to "2",
                          "2" to "3", 
                          "3" to "4", 
                          "4" to "5"))                                            //用于所有给定 key 都不存在时
        																		  //同时设置一个或多个 key-value 对
    }

}

获取值

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {
        stringRedisTemplate.opsForValue().get("1")                     //获取指定 key 的值(java写法)。
        stringRedisTemplate.opsForValue()["1"]                         //获取指定 key 的值(kotlin写法)。
        stringRedisTemplate.opsForValue().multiGet(listOf("1", "2"))   //获取指定的集合里的值,返回的集合的value顺序跟传入集合的key对应
       
        stringRedisTemplate.opsForValue().get("1", 0, 3)  //用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由
                                                          //start 和 end 两个偏移量决定(包括 start 和 end 在内)
                                                          //例子:若当前key为“1”的value为“kotlin”,get("1", 0, 3)返回值为“kotl”                                               
    }
}

骚操作

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {
        
        stringRedisTemplate.opsForValue().size("user")              //获取指定key的字符串的长度
        
        stringRedisTemplate.opsForValue().append("1", "kotlin")          //用于在给定key的value后追加内容,若key
                                                                         //①存在   -> 在原本的value后追加给定的值
                                                                         //②不存在 -> 则效果等同redis的SET
                                                                         //并返回当前redis内存中value的长度


        stringRedisTemplate.opsForValue().decrement("num")               //将当前key的value减一操作,并返回当前redis内存中value
                                                                         //若当前的value不为redis支持的递减操作,则抛出异常

        stringRedisTemplate.opsForValue().decrement("num",10)            //将当前key的value减去指定数值操作,并返回当前redis内存中value
                                                                         //若当前的value不为redis支持的递减操作,则抛出异常

        stringRedisTemplate.opsForValue().increment("num")               //将当前key的value增一操作,并返回当前redis内存中value
                                                                         //若当前的value不为redis支持的递减操作,则抛出异常

        stringRedisTemplate.opsForValue().increment("num",10)            //将当前key的value增加指定数值操作,并返回当前redis内存中value
                                                                         //若当前的value不为redis支持的递减操作,则抛出异常
        
        stringRedisTemplate.randomKey()                                  //从redis中随机选中一名幸运key并返回
        stringRedisTemplate.delete("user")        						 //删除指定key对应的key-value键值对,并返回删除结果
    }
}

列表

@SpringBootTest
class RedisApplicationTests {


    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {
    
        stringRedisTemplate
                .opsForList().leftPop("users")                                       //从列表的左侧弹出元素

        stringRedisTemplate                                                          //从列表的左侧弹出元素
                .opsForList().leftPop("users", 10, TimeUnit.SECONDS)                 //若该列表无元素,则最多阻塞该连接指定时间单位
                                                                                     //在指定时间单位若有元素插入,则左弹出,阻塞结束
                                                                                     //在指定时间单位若无元素插入,阻塞结束

        stringRedisTemplate                                                          //从列表的右侧压入该元素
                .opsForList().leftPush("user", "GTX")                                //并但会当前元素的位置

        stringRedisTemplate                                                          //如果该key存在,则从列表的右侧压入该元素
                .opsForList()                                                        //并但会当前元素的位置
                .leftPushIfPresent("user", "GTX")                                    //如果该key不存在,则不执行任何操作
                                      
        
        stringRedisTemplate
                .opsForList()                                                         //批量左插入
                .leftPushAll("user", "NVIDIA", "AMD", "ARM")
        
        stringRedisTemplate
                .opsForList()
                .rightPopAndLeftPush("users", "students")          //将第一个key列表的最右边元素弹出,插入到第二个key列表的最右侧
       
        
        stringRedisTemplate                                        //将第一个key列表的最右边元素弹出,插入到第二个key列表的最右侧
                .opsForList()                                      //若第一个key列表无元素,阻塞指定时间,在指定时间内有元素后则正常执行操作
                .rightPopAndLeftPush("users", "students", 10, TimeUnit.SECONDS)

        
        stringRedisTemplate											 //在指定列表的指定索引插入指定元素
               .opsForList().set("users",0,"kt")      
    }
}

哈希表

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {
        
        //在指定key放置指定的key-value
        stringRedisTemplate.opsForHash<String, String>().put("user", "id", "1100")
        stringRedisTemplate.opsForHash<String, String>().put("user", "username", "ls6666")
        stringRedisTemplate.opsForHash<String, String>().put("user", "password", "123456")
        //在指定key放置指定的key-value集合
        stringRedisTemplate.opsForHash<String, String>().putAll("user", mapOf("id" to "1000", "username" to "ls"))
        //若给定的hashKey不存在则在指定key放置指定的key-value,
        stringRedisTemplate.opsForHash<String, String>().putIfAbsent("user", "id", "1008611")


        //获取指定key的属性的value
        stringRedisTemplate.opsForHash<String, String>()["user", "id"]                     //kotlin
        stringRedisTemplate.opsForHash<String, String>().get("user", "id")    //Java
        //获取指定key的属性的value的集合
        stringRedisTemplate.opsForHash<String, String>().multiGet("user", listOf("id", "username"))


        //获取指定key的属性集合
        stringRedisTemplate.opsForHash<String, String>().keys("user")
        //获取指定key的值集合
        stringRedisTemplate.opsForHash<String, String>().values("user")
        //获取指定key的属性和值,其实就是返回了Map
        stringRedisTemplate.opsForHash<String, String>().entries("user")


        //获取指定key的k-v的个数
        stringRedisTemplate.opsForHash<String, String>().size("user")
        //获取指定key的属性的值的长度
        stringRedisTemplate.opsForHash<String, String>().lengthOfValue("user", "id")


        //删除指定key
        stringRedisTemplate.opsForHash<String, String>().delete("user")
        //删除指定key的某个属性的k-v
        stringRedisTemplate.opsForHash<String, String>().delete("user", "id")


        //对指定的key的属性进行增量操作
        stringRedisTemplate.opsForHash<String, String>().increment("user", "id", 10)
        
    }
}

集合

@SpringBootTest
class RedisApplicationTests {

    @Resource
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {

        //往集合添加元素
        stringRedisTemplate.opsForSet().add("user", "1", "2", "3", "3")

        //求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
        //求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
        //求两个集合不同的值:求第一个集合有而第二个集合没有的值!!!
        stringRedisTemplate.opsForSet().difference("type", "user")
        //求两个集合不同的值:求第一个集合有而第二个集合没有的值并存储到指定集合
        stringRedisTemplate.opsForSet().differenceAndStore("user", "type", "hello")

        //随机返回指定集合的指定个元素
        stringRedisTemplate.opsForSet().distinctRandomMembers("user", 2)


        //求集合之间交集
        stringRedisTemplate.opsForSet().intersect("type", "user")
        stringRedisTemplate.opsForSet().intersect("type", listOf("set1", "set2"))

        //求集合之间交集并存储到指定集合
        stringRedisTemplate.opsForSet().intersectAndStore(listOf("type", "user"), "newset")

        //该集合是否包含该元素
        stringRedisTemplate.opsForSet().isMember("user", "1")


        //返回该集合的所有元素
        stringRedisTemplate.opsForSet().members("user")
        //随机返回返回该集合的一个元素
        stringRedisTemplate.opsForSet().randomMember("user")
        //随机返回返回该集合的给定个元素
        stringRedisTemplate.opsForSet().randomMembers("user", 10)


        //将指定集合的某个元素移动至指定的集合
        stringRedisTemplate.opsForSet().move("user", "10", "move")


        //集合弹出所有元素
        stringRedisTemplate.opsForSet().pop("user")
        //集合弹出指定数量的元素
        stringRedisTemplate.opsForSet().pop("user", 2)

        //返回集合的大小
        stringRedisTemplate.opsForSet().size("user")


        //返回给定集合的并集
        stringRedisTemplate.opsForSet().union("user", "type")
        stringRedisTemplate.opsForSet().union("user", listOf("set1", "set2"))
        //将给定集合的并集并存储到指定集合
        stringRedisTemplate.opsForSet().unionAndStore("user", "type", "xxx")

    }
}

有序集合

@SpringBootTest
class RedisApplicationTests {


    @Resource
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {

        val hashSet = HashSet<ZSetOperations.TypedTuple<String>>()
        for (i in 1..20)
            hashSet.add(DefaultTypedTuple("$i", RandomUtil.randomDouble(1.0, 20.0)))


        //单个添加元素
        stringRedisTemplate.opsForZSet().add("lishuang", "ls", 20.0)
        //批量添加元素
        stringRedisTemplate.opsForZSet().add("user", hashSet)


        //返回指定集合的在score区间的元素的个数
        stringRedisTemplate.opsForZSet().count("user", 15.0, 18.0)

        //给指定结合的某个元素增量指定的score
        stringRedisTemplate.opsForZSet().incrementScore("user", "ls", 5.0)

        //返回指定位置区间的元素的集合
        stringRedisTemplate.opsForZSet().range("user", 1, 5)

        //返回指定score区间的元素的集合
        stringRedisTemplate.opsForZSet().rangeByScore("user", 1.0, 5.0)

        //删除该集合
        stringRedisTemplate.opsForZSet().remove("user")
        //删除该集合指定的元素
        stringRedisTemplate.opsForZSet().remove("user", "v1", "v2")
        //删除集合指定区间的元素
        stringRedisTemplate.opsForZSet().removeRange("user", 1, 5)
        
    }
}

常用操作

@SpringBootTest
class RedisApplicationTests {


    @Resource
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Test
    fun listTest() {

        //设置过期时间
        stringRedisTemplate.expire("user", 1, TimeUnit.MINUTES)
        //设置在指定日期过期
        stringRedisTemplate.expireAt("user", DateUtil.offsetDay(Date(), 1))
        //坚持下去!!!永不过期!!!
        stringRedisTemplate.persist("user")

        //获取到期时间(秒)
        stringRedisTemplate.getExpire("user")
        //获取到期时间并格式化指定单位
        stringRedisTemplate.getExpire("user", TimeUnit.HOURS)


        //判断是否有该key记录
        stringRedisTemplate.hasKey("user")


        //更新key的名字
        stringRedisTemplate.rename("user", "userX")
        stringRedisTemplate.renameIfAbsent("user", "userX")


        stringRedisTemplate.multi()   //开启事务
        //   ....(操作)....
        stringRedisTemplate.exec()    //提交事务


        //返回该key对应的数据结构
        stringRedisTemplate.type("user")


        //Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
        stringRedisTemplate.watch("user")
        stringRedisTemplate.watch(listOf("user1", "user2"))
        //取消监听
        stringRedisTemplate.unwatch()


    }
}

自定义RedisTemplate

序列化实体java类

实体类

data class User(var username:String?,var password:String?,var date: Date?):Serializable{ //不管是java还是kotlin一定要声明空构造器!!!
    constructor():this(null,null,null)
}

注入我们自定义的RedisTemplate

@Bean
fun userRedisTemplate(redisConnectionFactory:RedisConnectionFactory): RedisTemplate<String, User> {
    val redisTemplate = RedisTemplate<String, User>()        
    redisTemplate.keySerializer = StringRedisSerializer()  					         //key的序列化器
    redisTemplate.valueSerializer  = Jackson2JsonRedisSerializer(User::class.java)   //value的序列化器
    redisTemplate.setConnectionFactory(redisConnectionFactory)                       //配置好redis的连接
    return redisTemplate
}

使用

@SpringBootTest
class RedisApplicationTests {
    
    @Autowired
    private lateinit var userRedisTemplate: RedisTemplate<String, User>

    @Test
    fun listTest() {
        redisTemplate.opsForValue().set("user", User("ls", "666666", DateUtil.parseDate("1999-03-14")))
        redisTemplate.opsForValue().get("user")
    }
}

测试一下,成功,Perfect!

序列化字节数组

实体类

data class User(var username:String?,var password:String?,var date: Date?):Serializable{ //不管是java还是kotlin一定要声明空构造器!!!
    constructor():this(null,null,null)
}

注入我们自定义的RedisTemplate

@Bean
fun byteArrayRedisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<String, ByteArray> {
    val redisTemplate = RedisTemplate<String, ByteArray>()
    redisTemplate.keySerializer = StringRedisSerializer()
    redisTemplate.valueSerializer = JdkSerializationRedisSerializer()
    redisTemplate.setConnectionFactory(redisConnectionFactory)
    return redisTemplate
}

对象与字节数组互转工具

class SerializableTool {
    companion object {
        fun ObjectToByteArray(obj: Any?): ByteArray {
            val byteArrayOutputStream = ByteArrayOutputStream()
            val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
            objectOutputStream.writeObject(obj)
            objectOutputStream.flush()
            return byteArrayOutputStream.toByteArray()
        }

        fun ByteArrayToObject(byteArray: ByteArray?): Any {
            val `in` = ByteArrayInputStream(byteArray)
            val sIn = ObjectInputStream(`in`)
            return sIn.readObject()
        }
    }
}

使用

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    private lateinit var byteRedisTemplate: RedisTemplate<String, ByteArray>

    @Test
    fun listTest() {
        byteRedisTemplate.opsForValue().set("user", SerializableTool.ObjectToByteArray(User("ls", "6666", DateUtil.parseDate("1999/03/14"))))
        val user = SerializableTool.ByteArrayToObject(byteRedisTemplate.opsForValue().get("user")) as User
        println(user)   //User(username=ls, password=6666, date=1999-03-14 00:00:00)
    }
}

我们可以把任何东西存成数组放在redis里面,然后取出来自己进行序列化,就像我上面的序列化java类的例子,不过我的做法是不好的,好的做法是对于某种序列化我们应自定义RedisTemplate,序列化和反序列化时只要注入该RedisTemplate即可,不用考虑我上述的自定义序列化(SerializableTool)工具的使用。

Redis订阅-发布

原生写法

订阅端

语法:SUBSCRIBE channel [channel …]

连接上redis-cli,我们订阅一个名为kitty的信道:

SUBSCRIBE kitty

当前redis-cli客户端会在前台一直等待发布者发布消息,一旦有发布者在kitty这个信道上发布消息,这个订阅端就会收到消息显示在命令行上。

发布端

语法:PUBLISH channel message

连接上redis-cli,我们在kitty的信道上发布一个信息:

PUBLISH kitty  "hello redis!"

改命令会在kitty信道上发布一个消息“hello redis!”并返回收到该消息的订阅者的数量,订阅kitty的都会在第一时间收到该消息,很像UDP的特性

整合Springboot

消息封装
data class User(var username:String?,var password:String?,var date: Date?):Serializable{
    constructor():this(null,null,null)
}
自定义RedisTemplate
@Bean
fun userRedisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<String, User> {
    val redisTemplate = RedisTemplate<String, User>()
    redisTemplate.keySerializer = StringRedisSerializer()
    redisTemplate.valueSerializer = Jackson2JsonRedisSerializer(User::class.java)
    redisTemplate.setConnectionFactory(redisConnectionFactory)
    return redisTemplate
}
消息订阅监听
@Component
class RedisSubscriber : MessageListenerAdapter() {

    @Autowired
    private lateinit var redisTemplate: RedisTemplate<String, User>

    override fun onMessage(message: Message, bytes: ByteArray?) {
        //获取二进制消息体
        val body = message.body
        //获取信道二进制
        val channel = message.channel
        //反序列化消息体  使用的是jackson2JsonRedisSerializer
        val msg = redisTemplate.valueSerializer.deserialize(body)
        //反序列化channel名字  因为channel本来就是字符串  所以我们使用字符串序列化器  默认是UTF-8
        val topic = redisTemplate.stringSerializer.deserialize(channel)

        //因为序列化后是字符串类型,所以就会变成json,我们在通过工具转成Bean
        val user = JSONObject(msg).toBean(User::class.java)
        println(user)
        println("监听到topic为" + topic + "的消息:" + msg)

    }
}
将监听者与channel绑定
@Configuration
public class Redis {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //该订阅者订阅了信道channel1和channel2
        container.addMessageListener(listenerAdapter, new PatternTopic("channel1")); 
        container.addMessageListener(listenerAdapter, new PatternTopic("channel2"));
        return container;
    }
}
消息发布
@RestController
public class SampleController {

    @Autowired
    private RedisTemplate<String, User> redisTemplate;

    @GetMapping("/send")
    String send() {
        redisTemplate.convertAndSend("channel1", new User("lishuang", "jiayou0000", new Date()));
        return "success";
    }

}