Spring 3.1 RC1 – Cache Abstraction
Spring Cache Abstraction
Abordamos um das novas funcionalidades do Spring 3.1 RC1, profiles e environments. Ainda existem outras funcionalidades, mas hoje iremos dar uma olhada em Cache Abstraction.
Cache Abstraction é literalmente uma abstração out of the box para adicionar uma camada de cache sobre seus beans, usando uma arquitetura AOP para gerenciar o que deve e o que não deve ser feito o cache.
Usar a nova camada de cache é muito fácil se você já está habituado com Spring, e veremos uma das diferentes maneiras de configurar seus beans.
Baixando a Denpendência
Para quem utiliza maven, basta adicionar a seguinte dependência no pom do seu projeto:
[cc lang=”xml” width=”100%”]
org.springframework.maven.milestone
Spring Maven Milestone Repository
http://maven.springframework.org/milestone
org.springframework
spring-core
3.1.0.RC1
org.springframework
spring-context
3.1.0.RC1
[/cc]
Se você perferir, pode baixar os jar direto do site do spring.
Sem segredos aqui, basta adicionar as dependências ao projeto e está pronto para usar.
Entendendo o funcionamento
A maneira com que o cache funciona é bem simples. Você pode enxergar o cache como um mapa chave-valor, onde a chave é o conjunto de argumentos do seu método, e o valor é o valor devolvido pelo seu método. Pensando assim fica fácil entender o funcionamento que irei mostrar no exemplo.
Referente a configuração do Spring, é necessário instanciar um gerenciador de cache, ou na linguagem spring, cacheManager. Existe algumas implementações de cache manager disponível no spring, portanto iremos utilizar uma delas em nosso exemplo.
Vamos escrever um teste unitário com JUnit 4.8.1 para ilustrar o comportamento do cache.
Comece criando um arquivo padrão de beans do spring, mas com um namespace a mais:
[cc lang=”XML” width=”100%”]
[/cc]
Repare que estamos utilizando o namespace de cache além dos usuais.
A única configuração que iremos fazer aqui, é a do cache manager. Crie um bean da classe SimpleCacheManager e adicione o seguinte bean como cache gerenciado:
[cc lang=”XML” width=”100%”]
[/cc]
O que fizemos foi criar um cache manager simples do spring, e dentro configuramos o cache que iremos utilizar, associando a ele o nome de property. Repare também que estamos utilizando o cache implementado com um ConcurrentMap do java, garantindo o acesso thread safe ao cache.
Configuração por Anotação
Agora que configuramos o cache, vamos criar nossos beans que serão cacheados.
Vamos abordar primeiro a configuração por anotação em nossos beans. Para isso adicione a seguinte tag no seu xml de beans:
[cc lang=”XML” width=”100%”]
[/cc]
Assim será criado o proxy de cache em torno de todos os beans anotados.
Como não estou usando nenhuma biblioteca de proxy dinâmico, vamos criar uma interface para anotar nossas configurações:
[cc lang=”java” width=”100%”]
public interface CacheableTest {
@CacheEvict(value = “property”, allEntries = true)
void evictCache();
@Cacheable(“property”)
String getForCache(String s);
String getProperty();
void setProperty(String property);
}
[/cc]
Repare no getter e setter que usaremos no teste, e na anotação @Cacheable(“property”). Esta anotação está dizendo que o valor que este método devolver será armazenado no cache que configuramos previamente como property. Fato importante é que este valor é referente ao argumento passado como parâmetro no método.
A outra anotação @CacheEvict(value = “property”, allEntries = true) descreve o método que chamaremos para esvaziar o cache. Note que passamos o nome do cache, e que ele deve limpar todos os valores.
É importante notar também que pode ser passado mais de um nome de cache em ambas as anotações.
Uma implementação básica para nosso bean pode ser:
[cc lang=”java” width=”100%”]
@Component(“cacheBean”)
public class CacheableBean implements CacheableTest {
private String property;
public String getForCache(String s) {
return this.property;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public void evictCache() {
// Não faz nada
}
}
[/cc]
Estou usando o package scan para instanciar o bean. Agora nosso teste:
[cc lang=”java” width=”100%”]
public class CacheTest {
@Test
public void testCache() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
“spring31-test-beans.xml”);
CacheableTest bean = ctx.getBean(“cacheBean”, CacheableTest.class);
bean.setProperty(“teste”);
Assert.assertEquals(“teste”, bean.getProperty());
Assert.assertEquals(“teste”, bean.getForCache(“a”));
bean.setProperty(“teste2”);
Assert.assertEquals(“teste2”, bean.getProperty());
Assert.assertEquals(“teste”, bean.getForCache(“a”));
Assert.assertEquals(“teste2”, bean.getForCache(“b”));
bean.setProperty(“teste3”);
Assert.assertEquals(“teste3”, bean.getProperty());
Assert.assertEquals(“teste”, bean.getForCache(“a”));
Assert.assertEquals(“teste2”, bean.getForCache(“b”));
bean.evictCache();
bean.setProperty(“teste4”);
Assert.assertEquals(“teste4”, bean.getProperty());
Assert.assertEquals(“teste4”, bean.getForCache(“a”));
Assert.assertEquals(“teste4”, bean.getForCache(“b”));
}
}
[/cc]
O arquivo de beans se chama spring31-test-beans.xml.
Repare que estamos invocando o método getForCache passando alguns valores diferentes, e o valor devolvido é sempre igual, mesmo que depois setamos um valor diferente ao bean. Para atualizar o valor e limpar o cache, basta invocar o método evictCache que havíamos anotado com @CacheEvict.
Vale a pena brincar um pouco com o funcionamento do cache, e até mesmo criar outros caches e ver o comportamento do evict em diferentes métodos.
Últimas Considerações
As mesmas configurações que fizemos com anotações, pode-se fazer direto no XML. Não entrarei no detalhe pois o funcionamento é exatamente o mesmo, mas se você preferir basta olhar a documentação que é bem straightforward.
Lembre-se que esta camada de cache não possui nenhuma relação com o banco de dados, e deve ser usada com muito cuidado em tais casos, pois alguns erros de concistência podem aparecer devido a um cache desatualizado.
Vale a pena brincar com o cache manager, pois o spring suporta o uso do EhCache, o que pode ser muito útil se você já possui alguma configuração pré-definida para sua aplicação.
Por enquanto é isso, qualquer dúvida mande nos comentários que responderei assim que possível.
Por @Gust4v0_H4xx0r