2010-08-12 4 views
8

Ich möchte Singleton-Abhängigkeiten in Prototyp Spring Beans wiedereinführen, nachdem sie deserialisiert wurden.wie Singleton wieder anfügen Spring Bohnen nach Deserialisierung

Angenommen, ich habe eine Process-Bean, die von einer Repository-Bean abhängt. Die Repository-Bean ist ein Bereich wie ein Singleton, aber die Process-Bean ist ein Prototyp-Bereich. Periodisch serialisiere ich den Prozess und deserialisiere ihn später.

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

Ich möchte das Repository nicht serialisieren und deserialisieren. Ich möchte auch nicht "transient" auf die Membervariable setzen, die in Process einen Verweis darauf enthält, noch einen Verweis auf eine Art von Proxy oder etwas anderes als eine einfache alte Membervariable, die als Repository deklariert ist.

Was ich denke, ich möchte, dass der Prozess seine Abhängigkeit mit einem serialisierbaren Proxy gefüllt hat, der (mit einer vorübergehenden Referenz) auf das Repository zeigt und bei der Deserialisierung das Repository wieder finden kann. Wie könnte ich Spring dazu anpassen?

Ich glaube, ich könnte einen Proxy verwenden, um die Abhängigkeit Referenzen, ähnlich wie. Ich wünschte, ich könnte diese genaue Technik anwenden. Aber der Proxy, den ich Spring erzeugt habe, ist nicht serialisierbar, und die Dokumente sagen, dass ich eine Ausnahme bekomme, wenn ich sie mit einer Singleton-Bean verwende.

Ich könnte vielleicht einen benutzerdefinierten Bereich für die Singleton-Beans verwenden, der immer einen Proxy liefert, wenn er nach einer benutzerdefinierten Bean gefragt wird. Ist das eine gute Idee? Andere Ideen?

+0

In welchem ​​Anwendungskontext befinden sich diese Beans? Ein Webapp-Kontext? – skaffman

+0

Derzeit kein Webapp-Kontext. Später wäre wahrscheinlich ein Webapp-Kontext. – Ladlestein

+0

In diesem Fall, wie wird der Kontext bootstrapped? Ist es eine Desktop-Anwendung? – skaffman

Antwort

1

Wie wäre hinzugefügt Aspekte mit einem Injektionsschritt hinzufügen, wenn Sie das Objekt deserialisieren?

Sie würden AspectJ oder ähnliches dafür benötigen. Es würde sehr ähnlich wie die @Configurable-Funktion in Spring funktionieren.

z.B. fügen Sie einige Ratschläge rund um den einen „privaten void readObject- (Object in) throws IOException, ClassNotFoundException“ Methode

Dieser Artikel kann auch helfen: http://java.sun.com/developer/technicalArticles/Programming/serialization/

+0

Ich habe mich gegen Aspekte gewehrt, weil ich das Boot hier nicht rocken will, wenn Sie wissen, was ich meine, aber das ist ein Explorationsprojekt, vielleicht ist es jetzt an der Zeit. Dennoch frage ich mich über andere Möglichkeiten, es zu tun. – Ladlestein

1

Ich denke, die Idee, eine Bohne zu serialisieren und dann eine erneute Injektion von Abhängigkeiten zu erzwingen, ist nicht die beste Architektur.

Wie wäre es mit einer Art ProcessWrapper Bohne statt, die ein Singleton sein könnte. Es würde mit dem Repository injiziert und verwaltet entweder die Deserialisierung des Prozesses oder hat einen Setter dafür. Wenn ein neuer Prozess im Wrapper festgelegt wird, würde er setRepository() auf dem Prozess aufrufen. Die Beans, die den Prozess verwenden, können entweder vom Wrapper mit dem neuen Beer gesetzt werden oder den ProcessWrapper aufrufen, der an den Prozess delegiert wird.

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

Aber wie würde 'RepositoryFactory' funktionieren? Es muss serialisierbar sein und darf daher keinen Verweis auf den Anwendungskontext haben. Es schiebt nur das Problem herum. – skaffman

+0

Entschuldigung, richtig. Ich habe meine Antwort stark bearbeitet. – Gray

+0

Prozess und Repository sind nur Beispiele (und Prozess ist ein schlechtes Beispiel; sorry darüber). Die Beans, die deserialisiert werden, sind im Allgemeinen Controller, die eine bestimmte Interaktion zwischen dem Benutzer und unserer Webanwendung verwalten. Es gibt viele von ihnen, und es gibt immer mehr zu schreiben. Sie verbrauchen viele verschiedene Dienste. – Ladlestein

0

meine eigene Frage zu beantworten: wie ich das Problem gelöst habe, so weit ist um eine Basisklasse zu erstellen, die serialisiert und deserialisiert mit einem billigen kleinen Proxy. Der Proxy enthält nur den Namen der Bean.

Sie werden feststellen, dass es einen globalen Zugriff auf den Spring-Kontext verwendet; Eine elegantere Lösung könnte den Kontext in einer threadlokalen Variablen speichern.

Das resultierende serialisierte Objekt ist 150 Bytes oder so (wenn ich mich richtig erinnere).

3

benutzte ich diese stattdessen ganz ohne Proxy:

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); 
    } 
} 

Das Beispiel für eine Web-Umgebung ist, wenn der Anwendungsserver die Sitzung serialisiert, aber es sollte für jeden Application arbeiten.

3

Feder bietet eine Lösung für dieses Problem.

Werfen Sie einen Blick auf die Feder Dokumentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable.

7.8.1 AspectJ Verwendung einzuspritzen Domänenobjekten mit Feder

...

Der Träger soll, werden für Objekte außerhalb Abhängigkeits der Kontrolle jedes Behälters erzeugt eingesetzt. Domänenobjekte fallen oft in diese Kategorie, weil sie oft programmgesteuert mit dem neuen Operator oder durch ein ORM-Tool als Ergebnis einer Datenbankabfrage erstellt werden.

Der Trick besteht darin, Ladezeit Weben zu verwenden. Starten Sie einfach den jvm mit -javaagent: pfad/zu/org.springframework.instrument- {version} .jar. Dieser Agent erkennt jedes Objekt, das instanziiert wird, und wenn es mit @Configurable annotiert wird, konfiguriert es (inject @Autowired oder @Resource-Abhängigkeiten) dieses Objekt.

Ändern Sie einfach die Klasse Prozess zu

@Configurable 
class Process { 

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

Jedes Mal, wenn Sie eine neue Instanz erstellen

Process process = new Process(); 

Feder wird die Abhängigkeiten automatisch injizieren. Dies funktioniert auch, wenn das Process-Objekt deserialisiert wird.

+0

Warum nicht das Schlüsselwort 'transient' zur Variablen des Repository-Members hinzufügen? Andernfalls muss Ihre Repository-Klasse serialisiert werden. , aber ich sehe nicht, warum du es serialisieren willst. – herman

Verwandte Themen