No desenvolvimento de sistemas corporativos em Java, a cópia de atributos entre objetos de diferentes classes (como converter uma Entidade JPA para um DTO) é uma tarefa constante. Diversas bibliotecas facilitam esse trabalho, sendo as mais notáveis o Apache BeanUtils, o Spring BeanUtils e o Cglib BeanCopier. Embora tragam conveniência, a escolha da biblioteca impacta diretamente a performance da aplicação.
O uso do Apache BeanUtils é frequentemente desencorajado em guias de boas práticas de alta performance devido ao seu uso intensivo de introspecção e reflexão, o que resulta em uma execução consideravelmente mais lenta em comparação com as alternativas do Spring ou Cglib.
1. Configuração de Dependências
Para testar as diferentes abordagens, é necessário incluir as seguintes dependências no arquivo pom.xml do projeto Mavan:
<dependencies>
<!-- Framework Spring para BeanUtils -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.10</version>
</dependency>
<!-- Apache Commons BeanUtils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<!-- Cglib para BeanCopier -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
2. Definição das Classes de Modelo
Abaixo, definimos uma classe de origem com dados sensíveis e uma classe de destino simplificada para simular um cenário real de mapeamento.
public class EntradaDados {
private Long id;
private String nomeUsuario;
private Integer nivelAcesso;
private String senhaInterna;
private String tokenSecreto;
// Getters e Setters omitidos por brevidade
}
public class SaidaDados {
private Long id;
private String nomeUsuario;
private Integer nivelAcesso;
// Getters e Setters omitidos por brevidade
}
3. Imlpementação das Estratégias de Cópia
A classe a seguir encapsula os métodos para as três bibliotecas, incluindo uma estratégia de cache para o Cglib, que aumenta a eficiência ao reutilizar o objeto de cópia gerado.
import net.sf.cglib.beans.BeanCopier;
import org.apache.commons.beanutils.BeanUtils;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class GerenciadorDeCopia {
private static final Map<String, BeanCopier> CACHE_COPIER = new ConcurrentHashMap<>();
/**
* Utilizando Apache BeanUtils (Mais lento)
*/
public static void copiarComApache(Object destino, Object origem) {
try {
BeanUtils.copyProperties(destino, origem);
} catch (Exception e) {
throw new RuntimeException("Erro na cópia Apache", e);
}
}
/**
* Utilizando Spring BeanUtils (Performance moderada)
*/
public static void copiarComSpring(Object destino, Object origem) {
org.springframework.beans.BeanUtils.copyProperties(origem, destino);
}
/**
* Utilizando Cglib BeanCopier com Cache (Alta performance)
*/
public static void copiarComCglib(Object destino, Object origem) {
String chave = origem.getClass().getName() + "#" + destino.getClass().getName();
BeanCopier copiador = CACHE_COPIER.computeIfAbsent(chave, k ->
BeanCopier.create(origem.getClass(), destino.getClass(), false)
);
copiador.copy(origem, destino, null);
}
}
4. Demonstração e Verificação
Este exemplo demonstra a execução da cópia e valida se os campos correspondentes foram transferidos corretamente entre as instâncias.
public class Executora {
public static void main(String[] args) {
EntradaDados entrada = new EntradaDados();
entrada.setId(500L);
entrada.setNomeUsuario("DevMaster");
entrada.setNivelAcesso(99);
entrada.setSenhaInterna("123456");
SaidaDados saida = new SaidaDados();
// Executando a cópia via Cglib por ser a mais eficiente
GerenciadorDeCopia.copiarComCglib(saida, entrada);
System.out.println("Origem: " + entrada.getNomeUsuario() + ", ID: " + entrada.getId());
System.out.println("Destino: " + saida.getNomeUsuario() + ", ID: " + saida.getId());
}
}
Ao analisar o comportamento dessas ferramentas, observa-se que o Cglib se destaca por gerar bytecode em tempo de execução para realizar a atribuição de campos, evitando o overhaed de reflexão presente no Apache BeanUtils. O Spring BeanUtils oferece um meio-termo ideal para aplicações que já utilizam o ecossistema Spring e não possuem requisitos extremos de latência.