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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3180963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:11:46  来源:igfitidea点击:

Spring session-scoped beans (controllers) and references to services, in terms of serialization

javaspringserializationjsfservlets

提问by Bozho

  • a standard case - you have a controller (@Controller) with @Scope("session").
  • classes put in the session usually are expected to implement Serializableso that they can be stored physically in case the server is restarted, for example
  • If the controller implements Serializable, this means all services (other spring beans) it is referring will also be serialized. They are often proxies, with references to transaction mangers, entity manager factories, etc.
  • It is not unlikely that some service, or even controller, hold a reference to the ApplicationContext, by implementing ApplicationContextAware, so this can effectively mean that the whole context is serialized. And given that it holds many connections - i.e. things that are not serializable by idea, it will be restored in corrupt state.
  • 标准情况 - 您有一个@Controller带有@Scope("session").
  • 放在会话中的类通常需要实现,Serializable以便在服务器重新启动时可以物理存储它们,例如
  • 如果控制器实现Serializable,这意味着它引用的所有服务(其他 spring bean)也将被序列化。它们通常是代理,引用事务管理器、实体管理器工厂等。
  • 一些服务,甚至控制器,ApplicationContext通过实现ApplicationContextAware,持有对 ,的引用,这可能意味着整个上下文被序列化。并且鉴于它拥有许多连接 - 即无法通过想法序列化的东西,它将在损坏状态下恢复。

So far I've mostly ignored these issues. Recently I thought of declaring all my spring dependencies transientand getting them back in readResolve()by the static utility classes WebApplicationContextUtilsand such that hold the request/ServletContext in a ThreadLocal. This is tedious, but it guarantees that, when the object is deserialized, its dependencies will be "up to date" with the currentapplication context.

到目前为止,我大多忽略了这些问题。最近,我想声明我所有的 spring 依赖项,transientreadResolve()通过静态实用程序类将它们重新WebApplicationContextUtils引入,并将请求/ServletContext 保存在ThreadLocal. 这很乏味,但它保证,当对象被反序列化时,它的依赖项将与当前应用程序上下文“保持同步” 。

Is there any accepted practice for this, or any guidelines for serializing parts of the spring context.

是否有任何可接受的实践,或任何序列化 spring 上下文部分的指南。

Note that in JSF, managed beans (~controllers) are stateful (unlike action-based web frameworks). So perhaps my question stands more for JSF, than for spring-mvc.

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

采纳答案by Hans Westerbeek

In this presentation(around 1:14) the speaker says that this issue is resolved in spring 3.0 by providing a proxy of non-serializable beans, which obtains an instance from the currentapplication context (on deserialization)

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

回答by laher

I would expect to scope controllers as 'singleton', i.e. once per application, rather than in the session.

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

Session-scoping is typically used more for storing per-user information or per-user features.

会话范围通常更多地用于存储每个用户的信息或每个用户的功能。

Normally I just store the 'user' object in the session, and maybe some beans used for authentication or such. That's it.

通常我只是在会话中存储“用户”对象,也许还有一些用于身份验证等的 bean。就是这样。

Take a look at the spring docs for configuring some user data in session scope, using an aop proxy:

使用 aop 代理查看 spring 文档,用于在会话范围内配置一些用户数据:

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

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

Hope that helps

希望有帮助

回答by Thomas Kessler

I recently combined JSF with Spring. I use RichFaces and the @KeepAlive feature, which serializes the JSF bean backing the page. There are two ways I have gotten this to work.

我最近将 JSF 与 Spring 结合起来。我使用 RichFaces 和 @KeepAlive 功能,它序列化支持页面的 JSF bean。我有两种方法让这个工作。

1) Use @Component("session") on the JSF backing bean

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

2) Get the bean from ELContext when ever you need it, something like this:

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

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

回答by meriton

It appears that bounty didn't attract a single answer, so I'll document my limited understanding:

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

@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 will then not inject the naked MyService into IndexBean, but a serializable proxy to it. (I tested that, and it worked).

然后 Spring 不会将裸 MyService 注入 IndexBean,而是向它注入一个可序列化的代理。(我测试过,它奏效了)。

However, the spring documentation writes:

但是,spring 文档写道

You do notneed to use the <aop:scoped-proxy/>in conjunction with beans that are scoped as singletonsor prototypes. If you try to create a scoped proxy for a singleton bean, the BeanCreationExceptionis raised.

不是需要使用<aop:scoped-proxy/>与和作用域为豆类结合singletonsprototypes。如果您尝试为单例 bean 创建范围代理,BeanCreationException则会引发 。

At least when using java based configuration, the bean and its proxy can be instantiated just fine, i.e. no Exception is thrown. However, it looks like using scoped proxies to achieve serializability is not the intended use of such proxies. As such I fear Spring might fix that "bug" and prevent the creation of scoped proxies through Java based configuration, too.

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

Also, there is a limitation: The class name of the proxy is different after restart of the web application (because the class name of the proxy is based on the hashcode of the advice used to construct it, which in turn depends on the hashCode of an interceptor's class object. Class.hashCode does not override Object.hashCode, which is not stable across restarts). Therefore the serialized sessions can not be used by other VMs or across restarts.

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

回答by user1159790

After trying all the different alternatives suggested all I had to do was add aop:scoped-proxyto my bean definition and it started working.

在尝试了所有不同的替代方案后,我所要做的就是将aop:scoped-proxy添加到我的 bean 定义中,然后它就开始工作了。

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

securityService is injected into my managedbean which is view scoped. This seems to work fine. According to spring documentation this is supposed to throw a BeanCreationException since securityService is a singleton. However this does not seems to happen and it works fine. Not sure whether this is a bug or what the side effects would be.

securityService 被注入到我的托管bean 中,它是视图范围的。这似乎工作正常。根据 spring 文档,这应该抛出 BeanCreationException 因为 securityService 是单例。然而,这似乎不会发生,而且工作正常。不确定这是否是错误或副作用是什么。

回答by Martin Harm

Serialization of Dynamic-Proxiesworks well, even between different JVMs, eg. as used for Session-Replication.

动态代理的序列化效果很好,即使在不同的 JVM 之间,例如。用于会话复制。

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

You just have to set the id of the ApplicationContext beforethe context is refreshed (see: org.springframework.beans.factory.support.DefaultListableBeanFactory.setSerializationId(String))

您只需要刷新上下文之前设置 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();

Works fine on Spring-Version: 4.1.2.RELEASE

在 Spring 版本上工作正常:4.1.2.RELEASE