Item 90: Pense em usar proxies de serialização em vez de instâncias serializadas

Java Efetivo (livro) - Feb 12 - - Dev Community

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Método writeReplace na classe principal:
Substitui a instância original pelo proxy durante a serialização.

private Object writeReplace() {
    return new SerializationProxy(this);
}
Enter fullscreen mode Exit fullscreen mode

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!");
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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:

Image description

Image description

Image description

Image description

Image description

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .