web-dev-qa-db-fra.com

Beans à portée de session Spring (contrôleurs) et références aux services, en termes de sérialisation

  • un cas standard - vous avez un contrôleur (@Controller) avec @Scope("session").
  • les classes placées dans la session sont généralement censées implémenter Serializable afin qu'elles puissent être stockées physiquement au cas où le serveur serait redémarré, par exemple
  • Si le contrôleur implémente Serializable, cela signifie que tous les services (autres beans de printemps) auxquels il fait référence seront également sérialisés. Ce sont souvent des mandataires, avec des références aux gestionnaires de transactions, aux usines de gestionnaires d'entités, etc.
  • Il n'est pas improbable qu'un service, ou même un contrôleur, contienne une référence à ApplicationContext, en implémentant ApplicationContextAware, ce qui peut effectivement signifier que tout le contexte est sérialisé. Et étant donné qu'il contient de nombreuses connexions - c'est-à-dire des choses qui ne sont pas sérialisables par idée, il sera restauré dans un état corrompu.

Jusqu'à présent, j'ai surtout ignoré ces problèmes. Récemment, j'ai pensé à déclarer toutes mes dépendances de ressort transient et à les récupérer dans readResolve() par les classes d'utilitaires statiques WebApplicationContextUtils et telles que détiennent la requête/ServletContext dans un ThreadLocal. C'est fastidieux, mais cela garantit que, lorsque l'objet est désérialisé, ses dépendances seront "à jour" avec le contexte d'application actuel.

Existe-t-il une pratique acceptée pour cela, ou des directives pour sérialiser des parties du contexte de printemps?.

Notez que dans JSF, les beans gérés (~ contrôleurs) sont avec état (contrairement aux frameworks web basés sur l'action). Alors peut-être que ma question concerne plus JSF que Spring-mvc.

43
Bozho

Dans cette présentation (vers 1:14), l'orateur dit que ce problème est résolu au printemps 3.0 en fournissant un proxy de beans non sérialisables, qui obtient une instance de contexte d'application actuel (sur la désérialisation)

19
Hans Westerbeek

Il semble que la prime n'ait pas attiré une seule réponse, donc je documenterai ma compréhension limitée:

@Configuration
public class SpringConfig {

    @Bean 
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
    MyService myService() {
        return new MyService();
    }

    @Bean
    @Scope("request")
    public IndexBean indexBean() {
        return new IndexBean();
    }

    @Bean
    @Scope("request")
    public DetailBean detailBean() {
        return new DetailBean();
    }
}

public class IndexBean implements Serializable {

    @Inject MyService myService;

    public void doSomething() {
        myService.sayHello();
    }
}

public class MyService {
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

Spring n'injectera alors pas le MyService nu dans IndexBean, mais un proxy sérialisable. (J'ai testé cela, et cela a fonctionné).

Cependant, la documentation de printemps écrit :

Vous n'avez pas besoin d'utiliser le <aop:scoped-proxy/> conjointement avec des beans dont la portée est singletons ou prototypes. Si vous essayez de créer un proxy de portée pour un bean singleton, le BeanCreationException est levé.

Au moins lors de l'utilisation de Java, le bean et son proxy peuvent être instanciés très bien, c'est-à-dire qu'aucune exception n'est levée. Cependant, il semble que l'utilisation de proxys de portée pour atteindre la sérialisation ne soit pas l'utilisation prévue En tant que tel, je crains que Spring ne corrige ce "bogue" et empêche la création de proxys de portée via Java, également.

Il existe également une limitation: le nom de classe du proxy est différent après le redémarrage de l'application Web (car le nom de classe du proxy est basé sur le code de hachage des conseils utilisés pour le construire, qui à son tour dépend du code de hachage de un objet de classe d'intercepteur. Class.hashCode ne remplace pas Object.hashCode, qui n'est pas stable lors des redémarrages). Par conséquent, les sessions sérialisées ne peuvent pas être utilisées par d'autres machines virtuelles ou lors de redémarrages.

7
meriton

Je m'attends à ce que les contrôleurs soient définis comme `` singleton '', c'est-à-dire une fois par application, plutôt que dans la session.

La portée de la session est généralement plus utilisée pour stocker des informations par utilisateur ou des fonctionnalités par utilisateur.

Normalement, je stocke simplement l'objet "utilisateur" dans la session, et peut-être quelques beans utilisés pour l'authentification ou autre. C'est tout.

Jetez un œil aux documents de printemps pour configurer certaines données utilisateur dans la portée de la session, à l'aide d'un proxy aop:

http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-other-injection

J'espère que cela pourra aider

7
laher

J'ai récemment combiné JSF avec Spring. J'utilise RichFaces et la fonction @KeepAlive, qui sérialise le bean JSF sur la page. Il y a deux façons de faire fonctionner cela.

1) Utilisez @Component ("session") sur le bean support JSF

2) Obtenez le bean d'ELContext quand vous en avez besoin, quelque chose comme ceci:

@SuppressWarnings("unchecked")
public static <T> T  getBean(String beanName) {
    return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(FacesContext.getCurrentInstance().getELContext(), null, beanName);
}
2
Thomas Kessler

Après avoir essayé toutes les différentes alternatives suggérées, tout ce que j'avais à faire était d'ajouter aop: scoped-proxy à ma définition de bean et cela a commencé à fonctionner.

<bean id="securityService"
    class="xxx.customer.engagement.service.impl.SecurityContextServiceImpl">
    <aop:scoped-proxy/>
    <property name="identityService" ref="identityService" />
</bean>

securityService est injecté dans mon managedbean qui a une portée limitée. Cela semble bien fonctionner. Selon la documentation du printemps, cela est censé lever une BeanCreationException car securityService est un singleton. Cependant, cela ne semble pas se produire et cela fonctionne bien. Je ne sais pas s'il s'agit d'un bug ou quels seraient les effets secondaires.

2
user1159790

La sérialisation de Dynamic-Proxies fonctionne bien, même entre différentes JVM, par exemple. comme utilisé pour la réplication de session.

@Configuration public class SpringConfig {
@Bean 
@Scope(proxyMode = ScopedProxyMode.INTERFACES) 
MyService myService() {
    return new MyService();
}
.....

Il vous suffit de définir l'ID de ApplicationContext avant le contexte est actualisé (voir: org.springframework.beans.factory .support.DefaultListableBeanFactory.setSerializationId (String))

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// all other initialisation part ...
// before! refresh
ctx.setId("portal-lasg-appCtx-id");
// now refresh ..
ctx.refresh();
ctx.start();

Fonctionne bien sur Spring-Version: 4.1.2.RELEASE

1
Martin Harm