Atenção: Com as evoluções do Java e práticas modernas, existem alternativas mais seguras e eficientes para lidar com serialização/desserialização. Mas como estamos estudando o livro vamos considerar para aprender. VER NO TÓPICO FINAL SOBRE SERIALIZAÇÃO
Resumo do Item 90: Use Proxies de Serialização em vez de Instâncias Serializadas
Problemas da Serialização Direta
Riscos de segurança e bugs:
- Permite criar objetos sem passar por construtores normais.
- Violação de invariantes: Desserialização pode gerar instâncias inválidas.
- Ataques maliciosos: Streams de bytes manipuladas podem explorar vulnerabilidades.
O que é o Padrão de Proxy de Serialização?
Objetivo: Substituir a serialização nativa por uma abordagem segura e controlada.
Funcionamento:
- Cria uma classe proxy aninhada (serializável) que encapsula o estado da classe principal.
- Garante que apenas o proxy é serializado/desserializado, não a classe original.
Implementação do Proxy de Serialização
Criar a classe proxy:
- Classe estática privada, serializável, com campos idênticos à classe principal.
- Construtor recebe a instância original e copia seus dados.
private static class SerializationProxy implements Serializable {
private final Date start;
private final Date end;
SerializationProxy(Period p) {
this.start = p.start;
this.end = p.end;
}
}
Método writeReplace na classe principal:
Substitui a instância original pelo proxy durante a serialização.
private Object writeReplace() {
return new SerializationProxy(this);
}
Bloquear desserialização direta:
Adicionar readObject que lança exceção para evitar bypass do proxy.
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Use o proxy!");
}
Método readResolve no proxy:
Converte o proxy de volta em uma instância válida da classe principal.
private Object readResolve() {
return new Period(start, end); // Usa o construtor público
}
Vantagens do Padrão
Segurança reforçada:
- Elimina riscos de streams maliciosas e exposição de campos privados.
- Preserva imutabilidade: Campos final são mantidos sem restrições.
Flexibilidade:
- Permite alterar a implementação interna da classe sem quebrar compatibilidade (ex.: EnumSet).
- Simplicidade: Mais fácil de validar do que cópias defensivas.
Limitações
- Classes extensíveis: Não funciona bem se a classe pode ser herdada por usuários (viola encapsulamento).
- Grafos circulares: Pode causar ClassCastException em objetos com referências cíclicas.
- Desempenho: ~14% mais lento que serialização padrão (trade-off aceitável para segurança).
Conclusão
- Use proxies de serialização sempre que:
- A classe tem invariantes complexas.
- A segurança é crítica (ex.: dados sensíveis).
- Quer evitar códigos de validação/cópia defensiva manuais.
- Priorize este padrão sobre readObject/writeObject customizados para garantir robustez e imutabilidade real.
Chave: O proxy assegura que a desserialização sempre passa pelo construtor público, validando invariantes e bloqueando ataques!
Exemplos do Livro: