Domanda

Voglio reiniettare dipendenze Singleton con ambito nel prototipo bean Spring, dopo che sono stati deserializzato.

Di 'Ho un chicco di processo, che dipende da un fagiolo repository. Il fagiolo Repository è un ambito come Singleton, ma il fagiolo di processo è il prototipo con ambito. Periodicamente ho serializzare l'processo, e poi deserializzare esso.

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

Non voglio per serializzare e deserializzare il repository. Né voglio mettere "transitorio" sulla variabile membro che contiene un riferimento ad esso nel processo, né un riferimento ad un qualche tipo di proxy o qualcosa di diverso da una variabile semplice membro vecchia dichiarato come un repository.

Quello che penso che voglio è per il processo che la sua dipendenza piena di un proxy serializzabile che i punti (con un riferimento transitorio) al repository, e, su deserializzazione, possono ritrovare la repository. Come avrei potuto personalizzare primavera per farlo?

immagino che potrei utilizzare un proxy per contenere i riferimenti di dipendenza, molto simile. Vorrei poter usare quella tecnica esatta. Ma il proxy ho visto generare primavera non è serializzabile, e la documentazione dire che se lo uso con un fagiolo Singleton, vado a prendere un'eccezione.

I potrebbe utilizzare un ambito personalizzato, forse, sui fagioli Singleton, che sarebbe sempre fornire un proxy quando gli viene chiesto per un bean personalizzato con ambito. È che una buona idea? Altre idee?

È stato utile?

Soluzione

Come su aggiunto utilizzando aspetti per aggiungere una fase di iniezione quando si deserializzare l'oggetto?

Si avrebbe bisogno di AspectJ o simili per questo. Che avrebbe funzionato in modo molto simile alla funzione @Configurable in primavera.

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

In questo articolo può anche aiutare: http://java.sun.com/ developer / technicalArticles / Programmazione / serializzazione /

Altri suggerimenti

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.

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 di AspectJ alla dipendenza Iniettare oggetti di dominio con la Primavera

     

...

     

Il supporto è destinato ad essere utilizzato per l'esterno gli oggetti creati   del controllo di qualsiasi contenitore. Dominio oggetti spesso cadono in   questa categoria perché sono spesso creati a livello di codice   con il nuovo operatore, o tramite uno strumento ORM come risultato di una query di database.

Il trucco è quello di utilizzare il tempo di tessitura di carico. Basta avviare la JVM con -javaagent: path / to / 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.

Basta 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 inietta automaticamente le dipendenze. Questo funziona anche se l'oggetto processo viene deserializzato.

Credo che l'idea di serializzazione un fagiolo e poi forzando una reiniezione delle dipendenze non è la migliore architettura.

Che ne dite di avere una sorta di ProcessWrapper fagioli al posto che potrebbe essere un Singleton. Sarebbe iniettato il Repository eo gestisce la deserializzazione del processo o ha un setter per esso. Quando un nuovo processo viene impostato nella confezione, che avrebbe chiamato setRepository() sul processo. I fagioli che utilizzano il processo potrebbe essere impostati con quello nuovo dal wrapper o chiamare il ProcessWrapper che delegare 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);
   }
}

Rispondendo alla mia domanda: come ho risolto il problema finora è quello di creare una classe base che serializza e deserializza con un po 'di proxy a buon mercato. La delega contiene solo il nome del fagiolo.

Noterete che utilizza una globale per accedere al contesto di primavera; una soluzione più elegante potrebbe memorizzare il contesto in una variabile thread-locali, 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 giù di lì (se non ricordo male).

Il metodo applicationContext.getAutowireCapableBeanFactory().autowireBean(detachedBean); può essere utilizzato per riconfigurare un bean Spring gestiti che è stato serializzato e poi de-serializzato (i cui campi @Autowired diventano null). Vedere l'esempio di seguito. I dettagli di serializzazione sono omessi per semplicità.

public class DefaultFooService implements FooService {

    @Autowired
    private ApplicationContext ctx;

    @Override
    public SerializableBean bar() {
        SerializableBean detachedBean = performAction();
        ctx.getAutowireCapableBeanFactory().autowireBean(detachedBean);
        return detachedBean;
    }

    private SerializableBean performAction() {
        SerializableBean outcome = ... // Obtains a deserialized instance, whose @Autowired fields are detached.
        return outcome;
    }

}


public class SerializableBean {

    @Autowired
    private transient BarService barService;

    private int value;

    public void doSomething() {
        barService.doBar(value);
    }

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top