Pergunta

Eu tenho que trabalhar com um grande número de classes Java compiladas que não especificar explicitamente um serialVersionUID. Porque seus UIDs foram gerados arbitrariamente pelo compilador, muitas das classes que precisam ser serializado e desserializado acabam exceções causando, embora as definições de classe reais igualar-se. (Isso tudo é comportamento esperado, é claro.)

É impraticável para eu voltar atrás e corrigir tudo isso código 3-parte.

Portanto, a minha pergunta é: Existe alguma maneira de fazer o tempo de execução Java ignorar diferenças de serialVersionUIDs, e só não desserializar quando há diferenças reais na estrutura

Foi útil?

Solução

Se você tem acesso à base de código, você poderia usar o href="http://serialver.sourceforge.net/" rel="noreferrer"> tarefa serialver para inserir e modificar o serialVersionUID no código-fonte de uma classe serializável e corrigir o problema de uma vez por todas.

Se você não pode, ou se esta não é uma opção (por exemplo, se você já tiver serializado alguns objetos que você precisa deserialize), uma solução seria estender ObjectInputStream. Aumentar o seu comportamento para comparar a serialVersionUID do descritor de fluxo com o serialVersionUID da classe no JVM local que este descritor representa e usar o descritor de classe local em caso de incompatibilidade. Em seguida, basta usar esta classe personalizada para a desserialização. Algo como isto (créditos para esta mensagem ):

import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class DecompressibleInputStream extends ObjectInputStream {

    private static Logger logger = LoggerFactory.getLogger(DecompressibleInputStream.class);

    public DecompressibleInputStream(InputStream in) throws IOException {
        super(in);
    }

    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
        Class localClass; // the class in the local JVM that this descriptor represents.
        try {
            localClass = Class.forName(resultClassDescriptor.getName()); 
        } catch (ClassNotFoundException e) {
            logger.error("No local class for " + resultClassDescriptor.getName(), e);
            return resultClassDescriptor;
        }
        ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
        if (localClassDescriptor != null) { // only if class implements serializable
            final long localSUID = localClassDescriptor.getSerialVersionUID();
            final long streamSUID = resultClassDescriptor.getSerialVersionUID();
            if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
                final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
                s.append("local serialVersionUID = ").append(localSUID);
                s.append(" stream serialVersionUID = ").append(streamSUID);
                Exception e = new InvalidClassException(s.toString());
                logger.error("Potentially Fatal Deserialization Operation.", e);
                resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
            }
        }
        return resultClassDescriptor;
    }
}

Outras dicas

Como impraticável é isso para consertar? Se você tiver a fonte e pode reconstruir, você pode não apenas executar um script sobre o inteira base de código para inserir um

private long serialVersionUID = 1L;

em todos os lugares?

Use CGLIB para inseri-los nas classes binários?

Os erros de serialização em tempo de execução dizer explicitamente o que se espera que o ID seja. Basta alterar as suas aulas para declarar estes como o ID e tudo estará OK. Isto implica que você fazer mudanças, mas eu não acredito que isso pode ser evitado

Você poderia usar AspectJ para 'apresentar' o campo em cada classe serializável como ele é carregado. Gostaria em primeiro lugar introduzir uma interface de marcador em cada classe usando o pacote e, em seguida, introduzir o campo usando um hash do arquivo de classe para o serialVersionUID

public aspect SerializationIntroducerAspect {

   // introduce marker into each class in the org.simple package
   declare parents: (org.simple.*) implements SerialIdIntroduced;

   public interface SerialIdIntroduced{}

   // add the field to each class marked with the interface above.
   private long SerialIdIntroduced.serialVersionUID = createIdFromHash(); 

   private long SerialIdIntroduced.createIdFromHash()
   {
       if(serialVersionUID == 0)
       {
           serialVersionUID = getClass().hashCode();
       }
       return serialVersionUID;
   }
}

Você vai precisar adicionar o aspectj carga agente tecelão tempo para a VM para que ele possa tecer o conselho em suas classes 3rd party existentes. Sua embora engraçado quando você chegar ao redor para definir AspectJ para cima, sua notável o número de usos que você vai colocar a.

HTH

ste

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top