2012-05-16 15 views
7

Sto usando Spring e JSF 2 per creare un'applicazione web. Gli oggetti di business sono conservati nel contenitore primavera, e li ho iniettare nelle Fagioli gestito utilizzando il @ManagedProperty, in questo modo:Come re-iniettare un transitorio @ManagedProperty durante la deserializzazione?

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Getter @Setter 
    @ManagedProperty("#{someService}") 
    private SomeService someService; 
    // ... 

Il problema è che io continuo a ricevere un NotSerializableException per una classe da Spring (ServiceLocatorFactoryBean) che viene utilizzato dal bean SomeService.

Se lo faccio transient, come posso fare la re-iniezione di esso dopo la deserializzazione?

Oppure, quali sarebbero altri modi per risolvere questo problema?

Ho letto diverse altre domande simili qui, ma non ho trovato nessuna che si occupasse esattamente di questo problema.

+2

FYI: questo problema non esiste quando si utilizza solo EJB di Java EE anziché Spring. – BalusC

+0

@BalusC Sì, ne ho letto in altre domande, sfortunatamente non ne so abbastanza sugli EJB da usarlo ancora (e non so se potrei convincere i colleghi a lasciarmi provare in questo progetto) . Potresti indicarmi una buona risorsa per conoscerla, btw? – elias

+0

Non è così difficile. Assicurati che il tuo contenitore supporti già EJB (Glassfish, JBoss, Weblogic, ecc.). Annota la classe di servizio con '@ Stateless' o' @ Stateful' e inseriscilo con '@ EJB'. Questo è tutto. Nessun getter/setter richiesto btw. – BalusC

risposta

3

Invece di iniettare i bean Spring tramite elettroluminescenti in @ManagedProperty un'annotazione (eseguito sul inizializzazione ManagedBean), ottenere i fagioli valutano l'EL in fase di esecuzione.

Con questo approccio, questo è ciò che i fagioli JSF dovrebbe essere simile:

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return SpringJSFUtil.getBean("someService"); 
    } 
    // ... 

E la classe di utilità SpringJSFUtil.java che ottiene il fagiolo via EL:

import javax.faces.context.FacesContext; 

public class SpringJSFUtil { 

    public static <T> T getBean(String beanName) { 
     if (beanName == null) { 
      return null; 
     } 
     return getValue("#{" + beanName + "}"); 
    } 

    @SuppressWarnings("unchecked") 
    private static <T> T getValue(String expression) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     return (T) context.getApplication().evaluateExpressionGet(context, 
       expression, Object.class); 
    } 
} 

Questo elimina la proprietà del bean Spring (al costo di fare qualche altra valutazione EL), evitando così tutti i problemi di serializzazione di avere la proprietà al primo posto.

Lo stesso approccio, utilizzando OmniFaces:

Nel mio codice vero e proprio, io uso il metodo di un utility class disponibile da OmniFacesevaluateExpressionGet(String expression). Così, per quelli di voi che usano troppo, questo è ciò che il mio codice veramente assomigliare:

import static org.omnifaces.util.Faces.evaluateExpressionGet; 

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return evaluateExpressionGet("#{someService}"); 
    } 
    // ... 

noti che qui il metodo ottiene la piena EL ("# {espressione}"), non solo la primavera nome del bean (o si otterrebbe un ClassCastException).

-1

Bene tenere questo in mente dal manuale della molla ( link to spring):

Constructor-based o basati-setter DI?

Poiché è possibile combinare entrambi i DI basati su Constructor e Setter, è buona norma utilizzare gli argomenti del costruttore per le dipendenze e i setter obbligatori per le dipendenze opzionali. Si noti che l'uso di un'annotazione @Required su un setter può essere utilizzata per fare in modo che i setter richiedano delle dipendenze.

Il team Spring in genere consiglia l'iniezione di setter, perché un numero elevato di argomenti del costruttore può diventare poco pratico, specialmente quando le proprietà sono facoltative. I metodi di setter rendono anche oggetti di quella classe suscettibili di riconfigurazione o re-iniezione in seguito. La gestione tramite JMX MBeans è un caso d'uso convincente.

Alcuni puristi preferiscono l'iniezione basata su un costruttore. Fornire tutte le dipendenze oggetto significa che l'oggetto viene sempre restituito al codice client (chiamante) in uno stato totalmente inizializzato. Lo svantaggio è che l'oggetto diventa meno suscettibile di riconfigurazione e re-iniezione.

Utilizzare il DI che ha più senso per una particolare classe. A volte, quando si tratta di classi di terze parti a cui non si ha la fonte, la scelta è fatta per te. Una classe precedente non può esporre alcun metodo di setter, e quindi l'iniezione del costruttore è l'unica DI disponibile.

+0

Siamo spiacenti, non sto seguendo ... Che cosa ha a che fare il DI basato sul costruttore e basato sul setter? – elias

2

Provare @Scope (valore = BeanDefinition.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.INTERFACES) sul proprio Spring @Service. Questo dovrebbe iniettare un oggetto proxy serializzabile nel bean gestito che trasferirà il servizio all'accesso dopo la deserializzazione.

0

Per quelli da seguire - Ho avuto un problema simile con un ResourceBundle iniettato. Utilizzando parte della risposta di BalusC, ho fatto la seguente:

@ManagedProperty(value="#{myBundle}") 
private transient ResourceBundle myBundle; 

private Object readResolve() { 
    myBundle = FacesContext.getCurrentInstance().getApplication() 
     .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{myBundle}", 
     ResourceBundle.class); 
    return this; 
} 

In questo modo, il EL viene valutata solo quando il bean gestito viene deserializzato.

Problemi correlati