Spring会话范围的bean(控制器)和对服务的引用,就序列化而言

  • 一个标准的案例 – 你有一个控制器( @Controller )和@Scope("session")
  • 例如,放在会话中的类通常需要实现Serializable以便在服务器重新启动的情况下可以进行物理存储
  • 如果控制器实现Serializable ,这意味着它所指的所有服务(其他spring bean)也将被序列化。 他们通常是代理人,提及交易pipe理者,实体经理工厂等。
  • 某些服务甚至控制器通过实现ApplicationContextAware来持有对ApplicationContext的引用不是不太可能,所以这可以有效地表示整个上下文被序列化。 而且,由于它具有许多联系 – 即不能被想法序列化的东西,它将会在腐败状态下被恢复。

到目前为止,我一直忽略这些问题。 最近我想到了声明所有的spring依赖关系是transient并通过静态工具类WebApplicationContextUtils readResolve()它们返回到readResolve() ,并将请求/ ServletContext保存在ThreadLocal 。 这很乏味,但它保证,当对象被反序列化时,其依赖关系将与当前应用程序上下文“最新”。

有没有任何公认的做法,或任何序列化的spring上下文的指导方针。

请注意,在JSF中,托pipe的bean(〜控制器)是有状态的(与基于动作的Web框架不同)。 所以也许我的问题比JSF更适合spring-mvc。

在这个演示文稿中 (约1:14),演讲者说,这个问题在Spring 3.0中通过提供一个不可序列化bean的代理来解决,它从当前的应用上下文(反序列化)

我希望将控制器的范围定义为“单例”,即每个应用程序一次,而不是会话。

会话范围通常用于存储每个用户的信息或每个用户的function。

通常,我只是在会话中存储“用户”对象,也许有些bean用于身份validation等。 而已。

看一下spring文档,在会话范围中使用aop代理configuration一些用户数据:

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

希望有所帮助

看来,赏金没有吸引到一个单一的答案,所以我会logging我有限的理解:

 @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将不会将裸体MyService注入到IndexBean中,而是一个可序列化的代理。 (我testing过了,它工作)。

但是,春季文档写道 :

您不需要将<aop:scoped-proxy/>与限定为singletonsprototypes bean结合使用。 如果您尝试为单例bean创build一个范围代理,则引发BeanCreationException

至less在使用基于java的configuration时,bean和它的代理可以被实例化,也就是说不引发exception。 但是,看起来像使用作用域代理来实现可序列化不是这种代理的预期用途。 因此,我担心Spring可能会修复这个“bug”,并阻止通过基于Java的configuration创build范围代理。

此外,还有一个限制:重新启动Web应用程序后,代理的类名是不同的(因为代理的类名是基于用来构造它的通知的哈希码,而这又取决于哈希码的哈希码一个拦截器的类对象,Class.hashCode不会覆盖Object.hashCode,这在重启时是不稳定的)。 因此,序列化会话不能被其他VM使用或重新启动。

我最近把JSF和Spring结合起来了。 我使用RichFaces和@KeepAlivefunction,该function序列化支持页面的JSF bean。 有两种方法我已经得到这个工作。

1)在JSF支持bean上使用@Component(“session”)

2)在需要的时候从ELContext获取bean,如下所示:

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

尝试了所有不同的selectbuild议后,我所要做的就是添加aop:scoped-proxy到我的bean定义,并开始工作。

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

securityService被注入到我的托pipebean,这是视图作用域。 这似乎工作正常。 根据春季文档,这应该抛出一个BeanCreationException,因为securityService是一个单身人士。 但是,这似乎并没有发生,它工作正常。 不知道这是一个错误还是副作用。

Dynamic-Proxies的序列化运行良好,即使在不同的JVM之间也是如此。 用于会话复制。

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

您只需刷新上下文之前设置ApplicationContext的id(请参阅: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(); 

在Spring-Version:4.1.2.RELEASE上正常工作