REDIS学习(4)spring boot redisTemplate 对REDIS的简单封装,以及对引用包的说明,以及对序列化的详细说明

来源:互联网 发布:支持ubuntu的手机 编辑:程序博客网 时间:2024/06/03 21:16

综合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更像是一个数据库的操作

@Servicepublic class RedisService {@AutowiredRedisTemplate<?, ?> 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@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport {@Beanpublic CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {RedisCacheManager manager = new RedisCacheManager(redisTemplate);return manager;}@Beanpublic 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);}@Beanpublic 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();};}}

最后我们看一下Spring data redis定义的序列化接口和默认的JDK序列化的封装,代码比较整洁,我们可以从中学习

 [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;}@Overridepublic 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;}@Overridepublic 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> {@Overridepublic 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);}}}





0 0