■SpringMVC の小径 第7歩 Spring-MVC 迷いの一歩 |
7-3)Singletonの悪魔 |
丘を登り切って素晴らしい展望に心を洗われていたら、にわかに霧がかかり始めました。 周りを見渡して夕陽を背にしたところで、不思議な現象を目にしました。 霧の向こうに、虹の光輪に包まれた巨大な黒い人影が立ちはだかっています。 ブロッケンの妖怪が現れたようです。 自分の影が霧や雲のスクリーンに投影されて、巨大な人影が出現する現象です。 Spring-MVCで作成されたWebアプリケーションにも ブロッケン現象によく似た現象が発生します。 ブロッケンなら、人を怖がらせるだけで実害はないので放っておいても問題ありませんが こちらの現象は、Singletonの悪魔と呼ばれ(ほんとか?)、気を付けないと 自分の個人情報が人の手によって書き換えられてしまう恐ろしい事態が発生します。 前節の「7-2)入力チェック」で作成したプログラムをブラウザで動作させるとき、 二つのブラウザで同時に実行してみてください。 まず、一つ目のブラウザで適当に入力して回答してみます。 この状態で、二つ目のブラウザで同じURLを入力してみると。。。 あらら??、まだ何も入力されていないはずなのに、 一つ目のブラウザで入力した内容が表示されてしまっています。 一体なぜこんなことになってしまうのでしょう? 実は、Spring-MVCのコンポーネントスキャンで起動されたクラスはデフォルトでは全て、 Singleton(アプリケーション内で唯一無二のインスタンス)で起動される という特徴があります。 唯一無二のインスタンスなので、誰かがデータを投入した後で、 別の人がそのインスタンスにアクセスした場合、 前の人のデータがSingletonインスタンスに保持されているので、 そのままviewに表示されてしまう。ということになります。 Spring-MVCが、何故インスタンスをSingletonで実行させるのか 私もいろいろ調べてみたのですが明確な理由は不明のままです。 え~?、こんなんじゃ、Spring-MVCなんて使えないじゃん!!ヽ(`Д´)ノプンプン 大丈夫。ちゃんと悪魔祓いの儀式があるので、きちんとお祓いしておけば悪魔は近づけません。 前回作ったソースコードの内、コンポーネントスキャンされる コントローラ(Promenade.java)と、FormBean(PromenadeForm.java)の 2つのクラスに、悪魔払いのお札を貼ります。 |
FormBean(src/main/java/jp/dip/arimodoki/model/PromenadeForm.java) |
コントローラ(src/main/java/jp/dip/arimodoki/cntl/Promenade.java) |
ソースコードはどちらも前と全く同じで、違いはクラスの先頭に @Scope("prototype")アノテーションが一行追加されただけです。 これでリクエスト毎に別々のクラスインスタンスが生成されるので、他人に見られる心配はなくなります。 ほっと一安心。 |
ブロッケンの妖怪も、日暮れとともに姿を消しました。 あたりは次第に夜のとばりに包まれ始めたことなので、今回の散歩はひとまずここで終了です。 今日はほんとに長い散歩お疲れ様でした。 あれ? 最初のロードマップ(計画)では、まだMyBatisのコースが残っていたような気がしますが、 中止ですか? そーですね。 MyBatisコース歩いてませんね。 今日は時間が無くなってしまったということもありますが、 ここから先のコースは、スニーカーではちょっと歩けない山道になります。 それと、Eclipse以外に、データベース(PostgreSQL,MySQL,MariaDBなど)という 重い荷物を背負わないといけないし、新しくSQLという言葉を話せないと会話ができない 少し難しいコースになります。 私はまだ有給休暇が残っているので、今日はここで一泊して、 明日、MyBatis山に登ろうかと思っていますが、皆さんどうされますか? 参加は自由です。 参加する人は、明日までに準備をしておいてください。 それではひとまず解散です。 |
|
※)こぼれ話 2017/Apr/05 追記 Singltonの悪魔祓いのお札は、まぁ悪くはないのですが、 クラスの数が増えてくると一々お札を貼るのも面倒だし、時には貼り忘れることもあり なんか一発ですっきり排除する方法はないものか? と常々思っていたのですが 最近、やっと一斉排除の魔法を見つけたのでご紹介しておきます。 4-2)Spring-MVC設定 で、アプリケーション設定ファイル(applicationContext.xml)に、 コンポーネントスキャン宣言 <context:component-scan base-package="jp.dip.arimodoki.cntl"/> <context:component-scan base-package="jp.dip.arimodoki.blogic"/> <context:component-scan base-package="jp.dip.arimodoki.model"/> <context:component-scan base-package="jp.dip.arimodoki.mapper"/> <context:component-scan base-package="jp.dip.arimodoki.common"/> を記述しましたが、 この宣言に、ちょっと手を入れます。 <context:component-scan base-package="jp.dip.arimodoki" scope-resolver="org.springframework.context.annotation.Jsr330ScopeMetadataResolver" /> base-packageは、jp.dip.arimodoki配下全てを対象とするように、今回一行にまとめてしまいました。 scope-resolver属性を追加します。以上。 たったこれだけで、ここでコンポーネントスキャン宣言されたパッケージ配下のクラスのScopeは、prototypeになり 一々、@Scope("prototype")アノテーションを宣言しなくても、Singltonの呪縛から解放されます。 Jsr330ScopeMetadataResolverを使用するためには、正式にはpom.xmlに javax.injectプラグインを定義する必要がありますが、Mavenさんが依存関係を見て勝手に拾ってきてくれるので 明示的に宣言する必要はないみたいです。 もし、自動的に拾ってきてくれない場合は、 <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> を追加で定義してください。 |
7-2)入力チェック ![]() |
7-3)Singletonの悪魔 |
![]() |