2010-08-12 8 views
8

Desidero reintrodurre le dipendenze con ambito singleton in prototipi di bean Spring, dopo che sono stati deserializzati.come ricollegare i bean Spring singleton alla deserializzazione

Dire che ho un bean di processo, che dipende da un bean di repository. Il bean repository ha un ambito come un singleton, ma il bean Process ha un ambito prototipo. Periodicamente serializzo il processo e successivamente lo deserializzo.

class Process { 
    private Repository repository; 
    // getters, setters, etc. 
} 

Non desidero serializzare e deserializzare il repository. Né voglio mettere "transient" sulla variabile membro che contiene un riferimento ad esso in Process, né un riferimento a qualche tipo di proxy, o qualcosa di diverso da una semplice variabile membro vecchio dichiarata come repository.

Quello che penso che voglio è che il processo abbia la sua dipendenza riempita con un proxy serializzabile che punta (con un riferimento transitorio) al repository e, dopo la deserializzazione, può trovare nuovamente il repository. Come posso personalizzare Spring per farlo?

I figura potrei usare un proxy per contenere i riferimenti di dipendenza, molto simile. Vorrei poter usare quella tecnica esatta. Ma il proxy che ho visto generare Spring non è serializzabile, e i documenti dicono che se lo uso con un bean singleton, otterrò un'eccezione.

Potrei utilizzare un ambito personalizzato, forse, sui bean singleton, che fornirebbe sempre un proxy quando viene richiesto un bean con ambito personalizzato. È una buona idea? Altre idee?

+0

Che tipo di contesto applicativo sono questi fagioli in? Un contesto webapp? – skaffman

+0

In questo momento, non un contesto webapp. Più tardi, probabilmente sarebbe un contesto webapp. – Ladlestein

+0

In tal caso, come viene avviato il contesto? È un'applicazione desktop? – skaffman

risposta

1

In che modo aggiungere gli aspetti per aggiungere una fase di iniezione quando si deserializza l'oggetto?

Avresti bisogno di AspectJ o simili per questo. Funzionerebbe in modo molto simile alla funzione @Configurable in primavera.

ad es. aggiungere qualche consiglio intorno al un "readObject private void (ObjectInputStream in) throws IOException, ClassNotFoundException" metodo

Questo articolo può anche aiutare: http://java.sun.com/developer/technicalArticles/Programming/serialization/

+0

Ho resistito agli aspetti perché non voglio far scoppiare la barca qui, se capisci cosa intendo, ma questo è un progetto esplorativo, quindi forse ora è il momento. Ancora, mi chiedo altri modi per farlo. – Ladlestein

1

Penso che l'idea di serializzare un bean e quindi forzare una reintroduzione delle dipendenze non sia l'architettura migliore.

Come avere una specie di bean ProcessWrapper invece che potrebbe essere un singleton. Sarebbe iniettato con il repository e gestisce la deserializzazione del processo o ha un setter per esso. Quando un nuovo processo è impostato nel wrapper, chiamerebbe setRepository() sul processo. I bean che utilizzano il processo potrebbero essere impostati con il nuovo dal wrapper o chiamare ProcessWrapper che delegherebbe al processo.

class ProcessWrapper { 
    private Repository repository; 
    private Process process; 
    // getters, setters, etc. 

    public void do() { 
     process.do(); 
    } 

    public void setProcess(Process process) { 
     this.process = process; 
     this.process.setRepository(repository); 
    } 
} 
+0

Ma come funzionerebbe 'RepositoryFactory'? Deve essere serializzabile e quindi non può avere un riferimento al contesto dell'applicazione. Spinge semplicemente il problema. – skaffman

+0

Siamo spiacenti, giusto. Ho modificato pesantemente la mia risposta. – Gray

+0

Process and Repository sono solo esempi (e Process è un cattivo esempio, mi dispiace per quello). I bean che vengono deserializzati, in generale, sono controller che gestiscono una particolare interazione tra l'utente e la nostra webapp. Ce ne sono molti, e ce ne sono sempre di più da scrivere. Consumano molti servizi diversi. – Ladlestein

0

Rispondendo alla mia domanda: come ho risolto il problema finora è per creare una classe base che serializza e deserializza usando un piccolo proxy economico. Il proxy contiene solo il nome del bean.

Noterete che utilizza un accesso globale al contesto Spring; una soluzione più elegante potrebbe memorizzare il contesto in una variabile locale del thread, qualcosa del genere.

public abstract class CheaplySerializableBase 
    implements Serializable, BeanNameAware { 

    private String name; 

    private static class SerializationProxy implements Serializable { 

     private final String name; 

     public SerializationProxy(CheaplySerializableBase target) { 
      this.name = target.name; 
     } 

     Object readResolve() throws ObjectStreamException { 
      return ContextLoader.globalEvilSpringContext.getBean(name); 
     } 

    } 

    @Override 
    public void setBeanName(String name) { 
     this.name = name; 
    } 

    protected Object writeReplace() throws ObjectStreamException { 
     if (name != null) { 
      return new SerializationProxy(this); 
     } 
     return this; 
    } 
} 

L'oggetto serializzato risultante è 150 byte o così (se non ricordo male).

3

ho usato questo, invece, senza alcuna delega:

public class Process implements HttpSessionActivationListener { 
    ... 
    @Override 
    public void sessionDidActivate(HttpSessionEvent e) { 
     ServletContext sc = e.getSession().getServletContext(); 
     WebApplicationContext newContext = WebApplicationContextUtils 
      .getRequiredWebApplicationContext(sc); 
     newContext.getAutowireCapableBeanFactory().configureBean(this, beanName); 
    } 
} 

L'esempio è per un ambiente web quando il server di applicazione serializza la sessione, ma dovrebbe funzionare per qualsiasi ApplicationContext.

3

Primavera offre una soluzione per questo problema.

Date un'occhiata alla documentazione di primavera http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable.

7.8.1 Utilizzo AspectJ alla dipendenza oggetti di dominio iniettare Spring

...

Il sostegno è destinato a essere utilizzato per gli oggetti creati fuori del controllo di qualsiasi contenitore. oggetti di dominio spesso rientrano in questa categoria perché sono spesso creati a livello di codice utilizzando il nuovo operatore, o tramite uno strumento ORM come risultato di una query di database.

Il trucco è utilizzare la tessitura del tempo di caricamento. Basta avviare jvm con -javaagent: percorso/su/org.springframework.instrument- {versione} .jar. Questo agente riconosce ogni oggetto che viene creata un'istanza e se è annotato con @Configurable si configurerà (iniettare @Autowired o @Resource dipendenze) quell'oggetto.

solo cambiare la classe Process per

@Configurable 
class Process { 

    @Autowired 
    private transient Repository repository; 
    // getters, setters, etc. 
} 

Ogni volta che si crea una nuova istanza

Process process = new Process(); 

primavera inietterà automaticamente le dipendenze. Funziona anche se l'oggetto Processo è deserializzato.

+0

Perché non aggiungere la parola chiave 'transitoria' alla variabile membro repository? Altrimenti la classe del repository deve essere serializzabile. , ma non vedo perché vorresti serializzarlo. – herman

Problemi correlati