在序列化方面,春季会话范围的 Bean(控制器)和对服务的引用

2022-09-01 00:40:11
  • 标准情况 - 您有一个带有 的控制器 ()。@Controller@Scope("session")
  • 放置在会话中的类通常应该实现,以便在服务器重新启动时可以物理存储它们,例如Serializable
  • 如果控制器实现,这意味着它所引用的所有服务(其他弹簧豆)也将被序列化。它们通常是代理,参考交易经理,实体经理工厂等。Serializable
  • 某些服务甚至控制器通过实现 来持有对 的引用的可能性不大,因此这可以有效地表示整个上下文被序列化。鉴于它拥有许多连接 - 即无法按想法序列化的东西,它将在损坏状态下恢复。ApplicationContextApplicationContextAware

到目前为止,我基本上忽略了这些问题。最近,我想声明我所有的弹簧依赖关系,并通过静态实用程序类将它们放回去,以便将请求/ServletContext保存在.这很乏味,但它可以保证,当对象被反序列化时,其依赖项将与当前应用程序上下文“最新”。transientreadResolve()WebApplicationContextUtilsThreadLocal

是否有任何公认的做法,或者任何序列化春季上下文部分的指南。

请注意,在 JSF 中,受管 Bean(~控制器)是有状态的(与基于操作的 Web 框架不同)。因此,也许我的问题更适用于JSF,而不是spring-mvc。


答案 1

在本演示中(大约 1:14),演讲者说,通过提供不可序列化的 Bean 代理(从当前应用程序上下文(反序列化)获取实例,这个问题在 Spring 3.0 中得到了解决。


答案 2

赏金似乎没有吸引到一个答案,所以我将记录我有限的理解:

@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,而是将其序列化为可序列化的代理。(我测试了一下,它有效)。

但是,弹簧文档写道

不需要将 与作用域为 或 的 Bean 结合使用。如果尝试为单例 Bean 创建作用域代理,则会引发 。<aop:scoped-proxy/>singletonsprototypesBeanCreationException

至少在使用基于java的配置时,Bean及其代理可以很好地实例化,即不会引发异常。但是,看起来使用作用域内代理来实现可序列化并不是此类代理的预期用途。因此,我担心Spring可能会修复这个“错误”,并阻止通过基于Java的配置创建作用域代理。

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