web-dev-qa-db-ja.com

シリアル化の観点から見た、SpringセッションスコープのBean(コントローラー)とサービスへの参照

  • 標準的なケース-@Scope("session")を備えたコントローラー(_@Controller_)があります。
  • セッションに配置されたクラスは通常、Serializableを実装することが期待されているため、サーバーが再起動された場合にそれらを物理的に保存できます。
  • コントローラがSerializableを実装している場合、これは、コントローラが参照しているすべてのサービス(他のSpring Bean)もシリアル化されることを意味します。多くの場合、トランザクションマネージャ、エンティティマネージャファクトリなどを参照するプロキシです。
  • ApplicationContextを実装することにより、一部のサービスまたはコントローラーがApplicationContextAwareへの参照を保持する可能性は低くないため、これは事実上、コンテキスト全体がシリアル化されることを意味します。そして、それは多くの接続を保持していることを考えると、つまり、アイデアによってシリアル化できないものは、破損した状態で復元されます。

これまでのところ、これらの問題はほとんど無視してきました。最近、すべての春の依存関係transientを宣言し、__ function _)__に静的ユーティリティクラスWebApplicationContextUtilsなどでリクエスト/ ServletContextをThreadLocalに保持するように、それらをreadResolve()に戻すことを考えました。これは退屈ですが、オブジェクトがデシリアライズされると、その依存関係がcurrentアプリケーションコンテキストで「最新」になることが保証されます。

これについて受け入れられている慣習、または春のコンテキストの一部をシリアル化するためのガイドラインはありますか?.

JSFでは、マネージドBean(〜コントローラー)はステートフルです(アクションベースのWebフレームワークとは異なります)。したがって、おそらく私の質問は、spring-mvcではなく、JSFの方に立っています。

43
Bozho

このプレゼンテーション (約1:14)で、スピーカーは、この問題は(3.0からインスタンスを取得するシリアライズ不可能なBeanのプロキシを提供することで3.0春に解決されると言いますcurrentアプリケーションコンテキスト(逆シリアル化時)

19
Hans Westerbeek

バウンティは単一の答えを引き付けなかったようですので、私の限られた理解を文書化します。

@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に注入せず、シリアライズ可能なプロキシをそれに投入します。 (私はそれをテストしました、そしてそれはうまくいきました)。

ただし、春のドキュメント writes

あなたはしない<aop:scoped-proxy/>スコープがsingletonsまたはprototypesのBeanと組み合わせて。シングルトンBeanのスコーププロキシを作成しようとすると、BeanCreationExceptionが発生します。

少なくともJavaベースの構成を使用する場合、Beanとそのプロキシは正常にインスタンス化できます。つまり、例外はスローされません。ただし、スコープ化されたプロキシを使用して直列化可能性を実現することは、意図された用途ではないようですそのため、SpringがJavaベースの構成によっても)スコーププロキシの作成を防ぐことができるのではないかと恐れています。

また、制限もあります。プロキシのクラス名は、Webアプリケーションの再起動後は異なります(プロキシのクラス名は、プロキシの作成に使用されるアドバイスのハッシュコードに基づいているため、インターセプタのクラスオブジェクト。Class.hashCodeはObject.hashCodeをオーバーライドしません。したがって、シリアル化されたセッションは、他のVMや再起動後には使用できません。

7
meriton

私はコントローラを「シングルトン」としてスコープすることを期待します。つまり、セッションではなくアプリケーションごとに1回です。

セッションスコープは、通常、ユーザーごとの情報またはユーザーごとの機能を格納するために使用されます。

通常、私は 'user'オブジェクトをセッションに格納し、おそらく認証などに使用されるいくつかのBeanを格納します。それでおしまい。

Aopプロキシを使用して、セッションスコープで一部のユーザーデータを構成するための春のドキュメントをご覧ください。

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

それが役に立てば幸い

7
laher

最近、JSFとSpringを組み合わせました。私は、RichFacesと@KeepAlive機能を使用します。この機能は、ページをバッキングするJSF Beanをシリアライズします。これを機能させる方法は2つあります。

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);
}
2
Thomas Kessler

すべての異なる代替案を試した後、私がしなければならないすべてのことはaop:scoped-proxyを私のBean定義に追加することであり、それが機能し始めました。

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

securityServiceは、ビュースコープの管理対象Beanに挿入されます。これは正常に動作するようです。春のドキュメントによれば、securityServiceはシングルトンであるため、これはBeanCreationExceptionをスローするはずです。ただし、これは発生しないようで、正常に動作します。これがバグなのか、それとも副次的な影響なのかはわかりません。

2
user1159790

Dynamic-Proxiesのシリアル化は、異なるJVM間でもうまく機能します。セッションレプリケーションに使用されます。

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

ApplicationContextのIDを設定するだけですbeforeコンテキストが更新されます(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バージョンで正常に動作:4.1.2.RELEASE

1
Martin Harm