1. Adicionando Dependências Maven
Para intergar o Redis em uma aplicação Spring MVC, é necessário incluir as bibliotecas do cliente Redis (Jedis) e a abstração do Spring Data Redis no arquivo pom.xml do seu projeto. Além disso, para a serialização e desserialização de objetos Java para JSON no Redis, é recomendável adicionar uma biblioteca como o Jackson.
<!-- Cliente Redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- Integração Spring Data Redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.4.RELEASE</version>
</dependency>
<!-- Serializador JSON (ex: Jackson) para objetos complexos -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
2. Arquivo de Propriedades do Redis
Crie um arquivo de propriedades, por exemplo, configuracao-redis.properties, para gerenciar os parâmetros de conexão e configuração do pool do Redis. Isso facilita a manutenção e a adaptação a diferentes ambientes de implantação.
# Configurações do pool Jedis
redis.cache.maxIdle=300
redis.cache.maxTotal=600
redis.cache.maxWaitMillis=1000
redis.cache.testOnBorrow=true
# Configurações do servidor Redis
redis.server.port=6379
redis.server.password=
redis.server.database=0
redis.server.host=10.10.10.1
3. Configuração XML do Spring Data Redis
Configure os beans do Spring para a integração com o Redis. Este exemplo utiliza um arquivo XML, contexto-cache-redis.xml, para definir o JedisPoolConfig, JedisConnectionFactory e RedisTemplate. O RedisTemplate é a principal interface para interagir com o Redis no Spring, permitindo operações de alto nível. É crucial definir serializadores adequados para chaves e valores, garantindo que os objetos Java sejam armazenados e recuperados corretamente do Redis.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Carrega as propriedades do Redis -->
<context:property-placeholder location="classpath:configuracao-redis.properties"/>
<!-- Configuração do Pool de Conexões Jedis -->
<bean id="jedisPoolConfiguracao" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.cache.maxIdle}" />
<property name="maxTotal" value="${redis.cache.maxTotal}" />
<property name="maxWaitMillis" value="${redis.cache.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.cache.testOnBorrow}" />
</bean>
<!-- Fábrica de Conexões Jedis para o Spring Data Redis -->
<bean id="jedisConnectionFabrica"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.server.host}" />
<property name="port" value="${redis.server.port}" />
<property name="password" value="${redis.server.password}" />
<property name="database" value="${redis.server.database}" />
<property name="poolConfig" ref="jedisPoolConfiguracao" />
</bean>
<!-- Serializadores para o RedisTemplate, importantes para converter objetos Java para bytes e vice-versa -->
<bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<bean id="jsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
<!-- Usar Object.class para permitir a serialização de qualquer tipo, o Jackson inferirá o tipo -->
<constructor-arg type="java.lang.Class" value="java.lang.Object" />
</bean>
<!-- Template de Operações Redis, usando os serializadores definidos -->
<bean id="redisOperacoesTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFabrica" />
<property name="keySerializer" ref="stringSerializer" />
<property name="valueSerializer" ref="jsonRedisSerializer" />
<property name="hashKeySerializer" ref="stringSerializer" />
<property name="hashValueSerializer" ref="jsonRedisSerializer" />
<!-- Opcional: Habilita suporte a transações para operações atômicas -->
<property name="enableTransactionSupport" value="true" />
</bean>
</beans>
4. Classe Utilitária para Operações Redis
Para encapsular as operações de cache e facilitar o uso do Redis na aplicação, é reocmendável criar uma classe de serviço ou utilitária. Esta classe injeta o RedisTemplate e oferece métodos convenientes para manipulação de diferentes tipos de dados no Redis (Strings, Hashes, Sets e Lists), abstraindo os detalhes de baixo nível do cliente.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.Expiration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class GerenciadorRedis {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public GerenciadorRedis(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* Define o tempo de expiração para uma chave existente.
* @param chave A chave a ser modificada.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo (e.g., TimeUnit.SECONDS).
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirExpiracao(String chave, long tempo, TimeUnit unidadeTempo) {
try {
if (tempo > 0) {
redisTemplate.expire(chave, tempo, unidadeTempo);
}
return true;
} catch (Exception e) {
// Considerar logar a exceção aqui.
return false;
}
}
/**
* Obtém o tempo de expiração restante para uma chave.
* @param chave A chave.
* @return O tempo restante em segundos. Retorna -1 se a chave não tiver expiração ou -2 se não existir.
*/
public Long obterTempoExpiracao(String chave) {
return redisTemplate.getExpire(chave, TimeUnit.SECONDS);
}
/**
* Verifica se uma chave existe no Redis.
* @param chave A chave.
* @return true se a chave existe, false caso contrário.
*/
public Boolean chaveExiste(String chave) {
try {
return redisTemplate.hasKey(chave);
} catch (Exception e) {
return false;
}
}
/**
* Remove uma ou mais chaves do cache.
* @param chaves Um array de chaves a serem removidas.
*/
@SuppressWarnings("unchecked")
public void removerChaves(String... chaves) {
if (chaves != null && chaves.length > 0) {
if (chaves.length == 1) {
redisTemplate.delete(chaves[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(chaves));
}
}
}
// ============================ Operações String =============================
/**
* Obtém o valor associado a uma chave do tipo String.
* @param chave A chave.
* @return O valor.
*/
public Object obterValor(String chave) {
return chave == null ? null : redisTemplate.opsForValue().get(chave);
}
/**
* Define um valor para uma chave do tipo String.
* @param chave A chave.
* @param valor O valor.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirValor(String chave, Object valor) {
try {
redisTemplate.opsForValue().set(chave, valor);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Define um valor para uma chave do tipo String com tempo de expiração.
* @param chave A chave.
* @param valor O valor.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirValorComExpiracao(String chave, Object valor, long tempo, TimeUnit unidadeTempo) {
try {
if (tempo > 0) {
redisTemplate.opsForValue().set(chave, valor, tempo, unidadeTempo);
} else {
definirValor(chave, valor);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* Incrementa o valor numérico de uma chave String.
* @param chave A chave.
* @param incremento O valor a ser adicionado (deve ser maior que 0).
* @return O novo valor após o incremento.
*/
public Long incrementarValor(String chave, long incremento) {
if (incremento < 0) {
throw new IllegalArgumentException("O incremento deve ser maior que 0.");
}
return redisTemplate.opsForValue().increment(chave, incremento);
}
/**
* Decrementa o valor numérico de uma chave String.
* @param chave A chave.
* @param decremento O valor a ser subtraído (deve ser maior que 0).
* @return O novo valor após o decremento.
*/
public Long decrementarValor(String chave, long decremento) {
if (decremento < 0) {
throw new IllegalArgumentException("O decremento deve ser maior que 0.");
}
return redisTemplate.opsForValue().increment(chave, -decremento);
}
/**
* Define o valor de uma chave se ela não existir, com um tempo de expiração.
* Implementa o comportamento SET NX PX de forma idiomática com Spring Data Redis.
* @param chave A chave.
* @param valor O valor a ser definido (como String).
* @param tempoExpiracaoMillis O tempo de expiração em milissegundos.
* @return true se o valor foi definido, false caso contrário.
*/
public boolean definirSeNaoExiste(String chave, String valor, long tempoExpiracaoMillis) {
try {
Boolean result = redisTemplate.execute((RedisConnection connection) -> {
return connection.set(chave.getBytes(), valor.getBytes(),
SetOption.ifAbsent(),
Expiration.milliseconds(tempoExpiracaoMillis));
});
return Boolean.TRUE.equals(result);
} catch (Exception e) {
return false;
}
}
/**
* Obtém o valor de uma chave diretamente como String, sem serialização do RedisTemplate.
* Útil para valores definidos por SET NX PX que são strings puras.
* @param chave A chave.
* @return O valor associado à chave como String, ou null se ocorrer um erro ou a chave não existir.
*/
public String obterValorStringDireto(String chave) {
try {
return redisTemplate.execute((RedisConnection connection) -> {
byte[] valueBytes = connection.get(chave.getBytes());
return (valueBytes != null) ? new String(valueBytes) : null;
});
} catch (Exception e) {
return null;
}
}
// ================================ Operações Hash =================================
/**
* Obtém um valor de um campo dentro de um Hash.
* @param chaveHash A chave do Hash.
* @param campo O campo dentro do Hash.
* @return O valor do campo.
*/
public Object obterCampoHash(String chaveHash, String campo) {
return redisTemplate.opsForHash().get(chaveHash, campo);
}
/**
* Obtém todos os pares chave-valor de um Hash.
* @param chaveHash A chave do Hash.
* @return Um Map contendo todos os campos e seus valores.
*/
public Map<Object, Object> obterTodosCamposHash(String chaveHash) {
return redisTemplate.opsForHash().entries(chaveHash);
}
/**
* Define múltiplos campos e valores em um Hash.
* @param chaveHash A chave do Hash.
* @param mapaValores Um Map de campos (String) e seus valores (Object).
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirMultiplosCamposHash(String chaveHash, Map<String, Object> mapaValores) {
try {
redisTemplate.opsForHash().putAll(chaveHash, mapaValores);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Define múltiplos campos e valores em um Hash com tempo de expiração para o Hash.
* @param chaveHash A chave do Hash.
* @param mapaValores Um Map de campos (String) e seus valores (Object).
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirMultiplosCamposHashComExpiracao(String chaveHash, Map<String, Object> mapaValores, long tempo, TimeUnit unidadeTempo) {
try {
redisTemplate.opsForHash().putAll(chaveHash, mapaValores);
if (tempo > 0) {
definirExpiracao(chaveHash, tempo, unidadeTempo);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* Define um campo e valor em um Hash. Se a chave do Hash não existir, ela será criada.
* @param chaveHash A chave do Hash.
* @param campo O campo.
* @param valor O valor.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirCampoHash(String chaveHash, String campo, Object valor) {
try {
redisTemplate.opsForHash().put(chaveHash, campo, valor);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Define um campo e valor em um Hash com tempo de expiração para a chave do Hash.
* @param chaveHash A chave do Hash.
* @param campo O campo.
* @param valor O valor.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean definirCampoHashComExpiracao(String chaveHash, String campo, Object valor, long tempo, TimeUnit unidadeTempo) {
try {
redisTemplate.opsForHash().put(chaveHash, campo, valor);
if (tempo > 0) {
definirExpiracao(chaveHash, tempo, unidadeTempo);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* Remove campos específicos de um Hash.
* @param chaveHash A chave do Hash.
* @param campos Um ou mais campos a serem removidos.
*/
public void removerCamposHash(String chaveHash, Object... campos) {
redisTemplate.opsForHash().delete(chaveHash, campos);
}
/**
* Verifica se um campo existe em um Hash.
* @param chaveHash A chave do Hash.
* @param campo O campo.
* @return true se o campo existe, false caso contrário.
*/
public boolean campoHashExiste(String chaveHash, String campo) {
return redisTemplate.opsForHash().hasKey(chaveHash, campo);
}
/**
* Incrementa o valor numérico de um campo em um Hash.
* @param chaveHash A chave do Hash.
* @param campo O campo.
* @param incremento O valor a ser adicionado.
* @return O novo valor após o incremento.
*/
public Double incrementarCampoHash(String chaveHash, String campo, double incremento) {
return redisTemplate.opsForHash().increment(chaveHash, campo, incremento);
}
/**
* Decrementa o valor numérico de um campo em um Hash.
* @param chaveHash A chave do Hash.
* @param campo O campo.
* @param decremento O valor a ser subtraído.
* @return O novo valor após o decremento.
*/
public Double decrementarCampoHash(String chaveHash, String campo, double decremento) {
return redisTemplate.opsForHash().increment(chaveHash, campo, -decremento);
}
// ============================ Operações Set =============================
/**
* Obtém todos os membros de um Set.
* @param chaveSet A chave do Set.
* @return Um Set contendo todos os membros, ou null em caso de erro.
*/
public Set<Object> obterMembrosSet(String chaveSet) {
try {
return redisTemplate.opsForSet().members(chaveSet);
} catch (Exception e) {
return null;
}
}
/**
* Verifica se um membro existe em um Set.
* @param chaveSet A chave do Set.
* @param membro O membro.
* @return true se o membro existe, false caso contrário.
*/
public boolean setContemMembro(String chaveSet, Object membro) {
try {
return redisTemplate.opsForSet().isMember(chaveSet, membro);
} catch (Exception e) {
return false;
}
}
/**
* Adiciona um ou mais membros a um Set.
* @param chaveSet A chave do Set.
* @param membros Um ou mais membros.
* @return O número de membros adicionados.
*/
public Long adicionarMembrosSet(String chaveSet, Object... membros) {
try {
return redisTemplate.opsForSet().add(chaveSet, membros);
} catch (Exception e) {
return 0L;
}
}
/**
* Adiciona um ou mais membros a um Set com tempo de expiração para o Set.
* @param chaveSet A chave do Set.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @param membros Um ou mais membros.
* @return O número de membros adicionados.
*/
public Long adicionarMembrosSetComExpiracao(String chaveSet, long tempo, TimeUnit unidadeTempo, Object... membros) {
try {
Long count = redisTemplate.opsForSet().add(chaveSet, membros);
if (tempo > 0) {
definirExpiracao(chaveSet, tempo, unidadeTempo);
}
return count;
} catch (Exception e) {
return 0L;
}
}
/**
* Obtém o número de membros em um Set.
* @param chaveSet A chave do Set.
* @return O tamanho do Set.
*/
public Long obterTamanhoSet(String chaveSet) {
try {
return redisTemplate.opsForSet().size(chaveSet);
} catch (Exception e) {
return 0L;
}
}
/**
* Remove um ou mais membros de um Set.
* @param chaveSet A chave do Set.
* @param membros Um ou mais membros a serem removidos.
* @return O número de membros removidos.
*/
public Long removerMembrosSet(String chaveSet, Object... membros) {
try {
return redisTemplate.opsForSet().remove(chaveSet, membros);
} catch (Exception e) {
return 0L;
}
}
// =============================== Operações List =================================
/**
* Obtém um subconjunto de elementos de uma lista.
* @param chaveLista A chave da lista.
* @param inicio O índice inicial (0 para o primeiro elemento).
* @param fim O índice final (-1 para o último elemento).
* @return Uma lista de objetos, ou null em caso de erro.
*/
public List<Object> obterElementosLista(String chaveLista, long inicio, long fim) {
try {
return redisTemplate.opsForList().range(chaveLista, inicio, fim);
} catch (Exception e) {
return null;
}
}
/**
* Obtém o tamanho de uma lista.
* @param chaveLista A chave da lista.
* @return O tamanho da lista.
*/
public Long obterTamanhoLista(String chaveLista) {
try {
return redisTemplate.opsForList().size(chaveLista);
} catch (Exception e) {
return 0L;
}
}
/**
* Obtém um elemento de uma lista pelo índice.
* @param chaveLista A chave da lista.
* @param indice O índice (0 para o primeiro, -1 para o último).
* @return O elemento no índice especificado, ou null em caso de erro.
*/
public Object obterElementoListaPorIndice(String chaveLista, long indice) {
try {
return redisTemplate.opsForList().index(chaveLista, indice);
} catch (Exception e) {
return null;
}
}
/**
* Adiciona um elemento ao final de uma lista (push à direita).
* @param chaveLista A chave da lista.
* @param valor O valor a ser adicionado.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean adicionarElementoListaDireita(String chaveLista, Object valor) {
try {
redisTemplate.opsForList().rightPush(chaveLista, valor);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Adiciona um elemento ao final de uma lista com tempo de expiração para a lista.
* @param chaveLista A chave da lista.
* @param valor O valor a ser adicionado.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean adicionarElementoListaDireitaComExpiracao(String chaveLista, Object valor, long tempo, TimeUnit unidadeTempo) {
try {
redisTemplate.opsForList().rightPush(chaveLista, valor);
if (tempo > 0) {
definirExpiracao(chaveLista, tempo, unidadeTempo);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* Adiciona múltiplos elementos ao final de uma lista (push à direita).
* @param chaveLista A chave da lista.
* @param valores Uma lista de valores a serem adicionados.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean adicionarElementosListaDireita(String chaveLista, List<Object> valores) {
try {
redisTemplate.opsForList().rightPushAll(chaveLista, valores);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Adiciona múltiplos elementos ao final de uma lista com tempo de expiração para a lista.
* @param chaveLista A chave da lista.
* @param valores Uma lista de valores a serem adicionados.
* @param tempo O tempo de expiração.
* @param unidadeTempo A unidade de tempo.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean adicionarElementosListaDireitaComExpiracao(String chaveLista, List<Object> valores, long tempo, TimeUnit unidadeTempo) {
try {
redisTemplate.opsForList().rightPushAll(chaveLista, valores);
if (tempo > 0) {
definirExpiracao(chaveLista, tempo, unidadeTempo);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* Atualiza um elemento em uma lista pelo índice.
* @param chaveLista A chave da lista.
* @param indice O índice do elemento a ser atualizado.
* @param novoValor O novo valor.
* @return true se bem-sucedido, false caso contrário.
*/
public boolean atualizarElementoListaPorIndice(String chaveLista, long indice, Object novoValor) {
try {
redisTemplate.opsForList().set(chaveLista, indice, novoValor);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Remove 'contagem' ocorrências de 'valor' de uma lista.
* @param chaveLista A chave da lista.
* @param contagem O número de ocorrências a serem removidas.
* @param valor O valor a ser removido.
* @return O número de elementos removidos.
*/
public Long removerElementosLista(String chaveLista, long contagem, Object valor) {
try {
return redisTemplate.opsForList().remove(chaveLista, contagem, valor);
} catch (Exception e) {
return 0L;
}
}
}