综合1,2,3以及目前,我们所引用的redis包不过是
<dependency>
<groupId>org.springframework.boot</groupId><!-- 会附带引进jedis-2.7.3的包 -->
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
引用包至少有
spring-boot-starter-redis-1.3.5.RELEASE.jar
spring-data-redis-1.6.4.RELEASE.jar
jedis-2.7.3.jar
三个包
结合前面的第三节,redis都是跟Spring一起做为通用缓存接口使用
这一节使用的redisTemplate更像是一个数据库的操作
@Service
public class RedisService {
@Autowired
RedisTemplate<?, ?> redisTemplate;
/**获得客户端列表 */
public List<?> getClients(){
return redisTemplate.getClientList();
}
/**设置有超时时间的KV */
public Long set(String key, String value, long seconds) {
return redisTemplate.execute(c -> {
c.set(key.getBytes(), value.getBytes());
c.expire(key.getBytes(), seconds);
return 1L;
});
}
/**
*存入不会超时的KV
*/
public Long set(String key, String value) {
return redisTemplate.execute(c -> {
c.set(key.getBytes(), value.getBytes());
return 1L;
});
}
/**
* redis数据库条数
*/
public Long dbSize() {
return redisTemplate.execute(c -> c.dbSize());
}
public String ping() {
return redisTemplate.execute(c -> c.ping());
}
/**
* 删除所有指定数据库的数据
*/
public long flushDB() {
return redisTemplate.execute(c -> {
c.flushDb();
return 1L;
});
}
/**判断redis数据库是否有对应的key*/
public boolean exist(String key){
return redisTemplate.execute(c->c.exists(key.getBytes()));
}
/**获得redis数据库所有的key*/
public Set<String> keys(String pattern){
return redisTemplate.execute(c->c.keys(pattern.getBytes()).stream().map(this::getUTF).collect(Collectors.toSet()));
}
private String getUTF(byte[] data){
try {
return new String(data, "utf-8");
} catch (UnsupportedEncodingException e) {
LogCore.BASE.error("parse bytes err:{}", e);
return null;
}
}
}
使用:
@SuppressWarnings({ "unchecked", "rawtypes" })
public long save(UserInfo usrInfo) {
return redisTemplate.execute(c -> {
RedisSerializer key_slz = redisTemplate.getKeySerializer();
RedisSerializer slz = redisTemplate.getValueSerializer();
LogCore.BASE.info("key_slz={},slz={}",key_slz.getClass().getSimpleName(),slz.getClass().getSimpleName());
c.set(
key_slz.serialize(usrInfo.getClass().getSimpleName() + ":" + usrInfo.no),
slz.serialize(usrInfo));
return 1L;
});
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public UserInfo get(String no) {
return (UserInfo) redisTemplate.execute(c -> {
RedisSerializer key_slz = redisTemplate.getKeySerializer();
RedisSerializer slz = redisTemplate.getValueSerializer();
return slz.deserialize(c.get(key_slz.serialize(UserInfo.class.getSimpleName() + ":" + no)));
});
}
根据上一节内容,我们知道如果没有指定RedisTemplate,spring redis cache会选用javaAPI的序列化方式来将对象序列化,这种序列化方式性能一般,切后面增加字段会造成麻烦,我觉的比较合适的序列化方式有protocol buffer,JSON,带有递归的二进制字节流的方式等。
我们下面详细分析RedisTempalte这个类,StringRedisTemplate是RedisTemplate的唯一子类。
这个类很简单,我们甚至可以仿照此方法定义自己的MyRedisTemplate
[API]
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setValueSerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
setHashValueSerializer(stringSerializer);
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
注意,子类的构造方法总会默认调用父类的无参构造方法。
RedisTemplate默认定义了两个常用的序列化类
private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer();
以及 private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
我们可以如下注入我们的RedisTemplate,下面的例子将key的序列化方式定义为字符串,将value的序列化使用了jackson
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
RedisCacheManager manager = new RedisCacheManager(redisTemplate);
return manager;
}
@Bean
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(connectionFactory);
setMySerializer(template);
template.afterPropertiesSet();
LogCore.BASE.info("template{}" ,ReflectionToStringBuilder.toString(template, ToStringStyle.SHORT_PREFIX_STYLE));
return template;
}
/**
* 设置序列化方法
*/
private void setMySerializer(RedisTemplate template) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setKeySerializer(template.getStringSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
}
@Bean
public KeyGenerator smpkeyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getSimpleName()).append(":");//执行方法所在的类
sb.append(Stream.of(params).map(String::valueOf).collect(Collectors.joining("_")));
return sb.toString();
};
}
}
[API]
public interface RedisSerializer<T> {
byte[] serialize(T t) throws SerializationException;
T deserialize(byte[] bytes) throws SerializationException;
}
[API]
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
public Object deserialize(byte[] bytes) {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
public byte[] serialize(Object object) {
if (object == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
throw new SerializationException("Cannot serialize", ex);
}
}
}
[API]
public interface Converter<S, T> {
T convert(S source);
}
[API]
public class SerializingConverter implements Converter<Object, byte[]> {
private final Serializer<Object> serializer;
public SerializingConverter() {
this.serializer = new DefaultSerializer();
}
public SerializingConverter(Serializer<Object> serializer) {
Assert.notNull(serializer, "Serializer must not be null");
this.serializer = serializer;
}
@Override
public byte[] convert(Object source) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);
try {
this.serializer.serialize(source, byteStream);
return byteStream.toByteArray();
}
catch (Throwable ex) {
throw new SerializationFailedException("Failed to serialize object using " +
this.serializer.getClass().getSimpleName(), ex);
}
}
}
[API]
public class DeserializingConverter implements Converter<byte[], Object> {
private final Deserializer<Object> deserializer;
public DeserializingConverter() {
this.deserializer = new DefaultDeserializer();
}
public DeserializingConverter(ClassLoader classLoader) {
this.deserializer = new DefaultDeserializer(classLoader);
}
public DeserializingConverter(Deserializer<Object> deserializer) {
Assert.notNull(deserializer, "Deserializer must not be null");
this.deserializer = deserializer;
}
@Override
public Object convert(byte[] source) {
ByteArrayInputStream byteStream = new ByteArrayInputStream(source);
try {
return this.deserializer.deserialize(byteStream);
}
catch (Throwable ex) {
throw new SerializationFailedException("Failed to deserialize payload. " +
"Is the byte array a result of corresponding serialization for " +
this.deserializer.getClass().getSimpleName() + "?", ex);
}
}
}
[API]序列化writeObject
public class DefaultSerializer implements Serializer<Object> {
@Override
public void serialize(Object object, OutputStream outputStream) throws IOException {
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
}
}
[API]反序列化是用类加载器
public class DefaultDeserializer implements Deserializer<Object> {
private final ClassLoader classLoader;
public DefaultDeserializer() {
this.classLoader = null;
}
<span style="color:#ff0000;"> public DefaultDeserializer(ClassLoader classLoader) {
this.classLoader = classLoader;
}</span>
@Override
@SuppressWarnings("resource")
public Object deserialize(InputStream inputStream) throws IOException {
ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, this.classLoader);
try {
return objectInputStream.readObject();
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to deserialize object type", ex);
}
}
}