您的当前位置:首页正文

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

2024-11-07 来源:个人技术集锦

综合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();
		};
	}
}

最后我们看一下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;
	}

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

}





显示全文