Jedis ✍
Jedis是一个高性能的开源Java客户端,是Redis官方推荐的Java开发工具。
创建一个maven项目,配置如下:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
cd 到pom所在文件夹下:
mvn -f pom.xml dependency:copy-dependencies
数据操作
以下代码展示部分操作方式:
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;
public class JedisDemo {
@Test
public void operateString(){
System.out.println("-----------Jedis String 相关命令测试-----------");
Jedis jedis = new Jedis("xxx.xxx.xxx.xxx",6379);
jedis.auth("xxxxx");
System.out.println("Jedis.ping():"+jedis.ping());
jedis.set("key0","123456");
System.out.println("jedis.get(key0)"+jedis.get("key0"));
jedis.mset("key1","val1","key2","val2");
System.out.println("jedis.get(key*)"+jedis.keys("key*"));
System.out.println("jedis.get(key1)):"+jedis.get("key1"));
System.out.println("返回key的长度:"+jedis.strlen("key0"));
System.out.println("追加字符串:"+jedis.append("key0","app"));
System.out.println("打印key0:"+jedis.get("key0"));
System.out.println("---------------------------------------------");
jedis.close();
}
@Test
public void operateList(){
System.out.println("-----------Jedis List 相关命令测试-----------");
Jedis jedis = new Jedis("xxx.xxx.xxx.xxx",6379);
jedis.auth("xxxxx");
System.out.println("Jedis.ping():"+jedis.ping());
jedis.del("list1");
//从List尾部添加3个元素
jedis.rpush("list1","alice","bob","cindy");
System.out.println("获取类型:"+jedis.type("list1"));
System.out.println("遍历区间[0.-1],获取全部的元素:"+jedis.lrange("list1",0,-1));
System.out.println("获取List的长度:"+jedis.llen("list1"));
System.out.println("---------------------------------------------");
jedis.close();
}
@Test
public void oprateHash(){
System.out.println("-----------Jedis Hash 相关命令测试-----------");
Jedis jedis = new Jedis("xxx.xxx.xxx.xxx",6379);
jedis.auth("xxxxx");
jedis.del("config");
jedis.hset("config","ip","127.0.0.1");
System.out.println("获取Hash的Field关联的Value:"+jedis.hget("config","ip"));
System.out.println("获取类型:"+jedis.type("config"));
//批量添加field-value对
Map<String,String> configFields = new HashMap<>();
configFields.put("port","8080");
configFields.put("maxalive","3600");
configFields.put("weight","1.0");
//执行批量添加
jedis.hmset("config",configFields);
System.out.println("批量获取:"+jedis.hgetAll("config"));
System.out.println("获取所有的key:"+jedis.hkeys("config"));
System.out.println("获取所有的val:"+jedis.hvals("config"));
System.out.println("获取长度:"+jedis.hlen("config"));
System.out.println("---------------------------------------------");
jedis.close();
}
}
运行结果:
事务操作
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class JedisTransTest {
/**
* Jedis的事务测试
*/
public static void main(String[] args){
Jedis jedis = new Jedis("49.235.200.38",6379);
jedis.auth("2472500609cn#");
System.out.println("Jedis.ping():"+jedis.ping());
JSONObject jsonObject = new JSONObject();
jsonObject.put("name1","alice");
jsonObject.put("name2","bob");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
jedis.flushDB();
//jedis.watch(result);
try {
multi.set("user1",result);
multi.set("user2",result);
multi.exec(); //执行事务
}catch (Exception e){
multi.discard();//放弃事务
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}
JedisPool
数据库连接的底层是一条Socket通道,其创建和销毁很耗时间,需要有三次握手和四次挥手。
在数据库连接过程中,为了防止数据库连接的频繁创建、销毁带来的性能损耗,常常会用到连接池。
例如淘宝的Druid连接池、Tomcat的DBCP连接池。
SpringBoot整合Redis ✍
依赖
创建一个SpringBoot的项目,pom.xml文件redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
ctrl键
: spring-boot-starter-data-redis
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.4.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.0.4.RELEASE</version>
<scope>compile</scope>
</dependency>
发现有lettuce但没有jedis
在SpringBoot2.x之后,原本使用的jedis被替换为了lettuce。
- jedis:采用直连,多个线程操作的话是不安全的,如果要避免不安全的情况,要使用JedisPool连接池,像BIO,又有其他问题。
- lettuce:底层采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程的数量,更像NIO模式。
自动配置
SpringBoot所有的配置类都有一个自动配置类,
自动配置类都会绑定一个properties配置文件。
打开RedisAutoConfiguration,源码如下:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate
(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate
(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@EnableConfigurationProperties({RedisProperties.class})
用在配置类的注解,开启RedisProperties类的属性配置功能,将RedisProperties组件自动注入到容器中。
RedisProperties源码截图:
RedisProperties类上的注解:
@ConfigurationProperties(prefix = "spring.redis")
这个注解让类与配置文件application.properties绑定
类里的属性都是可以配置的内容:
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private RedisProperties.ClientType clientType;
private RedisProperties.Sentinel sentinel;
private RedisProperties.Cluster cluster;
private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();
配置类
RedisAutoConfiguration有两个模板方法:
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)//可以自己定义一个redisTemplate来替换这个默认的
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//默认的RedisTemplate没有过多的设置,redis对象需要序列化
//两个泛型都是Object,Object的类型,使用需要强制转换为String,Object
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
//String是redis中最常使用的类型,所以单独一个方法
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
配置文件:
这里有个点需要注意一下,如果你想连接远程的Redis服务器的话,redis.conf文件里:
这个要注释掉,不然会连接失败。
测试:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class SpnnredisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//获取redis的连接对象
RedisConnection connection =
redisTemplate.getConnectionFactory().getConnection();
System.out.println(connection.ping());
connection.flushDb();
//字符串操作
redisTemplate.opsForValue().set("key1","ablice");
System.out.println(redisTemplate.opsForValue().get("key1"));
//List操作
//redisTemplate.opsForList();
//Set操作
//redisTemplate.opsForSet();
//balabala好多操作
}
}
源码分析
再来看RedisAutoConfiguration类的注解:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() { }
注意最后一个@Import
:
//将指定类型的组件导入
//给容器自动创建出指定类的无参构造器返回的组件
@Import({User.class,XXX.class})
这里@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
就是自动创建Lettucexxx、Jedisxxx类的无参构造器返回的组件。不过默认是LettuceConnectionConfiguration生效。
再看redisTemplate方法,返回的是一个RedisTemplate类。
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
查看RedisTemplate类源码,截取部分:
自定义RedisTemplate
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* 可以当作一个固定模板
*/
@Configuration
public class RedisConfig {
//从RedisAutoConfiguration复制模板过来
//修改成自己的
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//为了平时开发方便,使用<String,Object>
RedisTemplate<String, Object> template = new RedisTemplate<>();
//连接Redis工厂
template.setConnectionFactory(redisConnectionFactory);
//序列化操作 Json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//转译
ObjectMapper objectMapper = new ObjectMapper();
//方便的方法允许更改底层VisibilityCheckers的配置,以更改自动检测哪些属性的详细信息
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
//把所有的properties Set进去
template.afterPropertiesSet();
return template;
}
}
@SpringBootTest
class SpnnredisApplicationTests {
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
@Test
public void testU() throws JsonProcessingException {
redisTemplate.opsForValue().set("user1", new User("alice",1));
Object user1 = redisTemplate.opsForValue().get("user1");
System.out.println(user1);
}
}
没有乱码了。
平时开发可以创建一个RedisUtil类去封装操作。
@SpringBootTest
class SpnnredisApplicationTests {
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
@Autowired
private RedisUtil redisUtil;
@Test
public void testUtil(){
redisUtil.set("name","alice");
System.out.println(redisUtil.get("name"));
}
}