1
これからSpringを使う開発者が
知っておくべきこと
2018/10/31
日本Springユーザ会
土岐 孝平
ハッシュタグ: #sf_11
自己紹介
• 土岐 孝平
• Springを使用したシステム開発の支援
• JavaやSpringの研修の講師
• 書籍の執筆
2
[改訂新版]Spring入門
はじめに
• 想定する聴講者
– Springを使ったプロジェクトに参加しているけれど、
Springの「おまじない」が何のためなのか分かっていない
• @Serviceって何?@Autowiredって何?
• 一応動いているけど、おかしな使い方になってないだ
ろうか?
• 発表の趣旨
– Springの初心者が疑問に思ったり、おかしな使い方をし
てしまいがちなところをピックアップして説明。
– 実プロジェクトで活かしてほしい。
– Springを学習するとっかかりになってほしい。
3
アジェンダ
• 「おまじない」がやっていること
– @Serviceや@Autowiredがやっていること
– @Transactionalの裏側
• おかしな作りになっていませんか?
– スレッドセーフになっていますか?
– トランザクションの入れ子はうまくできていますか
?
– トレースログを明示的に出力していませんか?
4
「おまじない」がやっているこ
と
5
「@Service」の意味
• 「Springにオブジェクトを作ってもらって、DIコンテナ※1で管理
してほしい」と宣言するアノテーション
• DIコンテナ起動時に、DIコンテナで管理された状態になる。
• DIコンテナで管理されたオブジェクトのことを、Beanと呼ぶ。
6
DIコンテナ
FooServiceオブジェクト
※1:DIコンテナ
Springが提供するオブジェクトの入れ物。DIコンテナでオブジェクトを管理す
ると、DIやAOPの機能を利用することができる。
@Service
FooServiceクラス
Bean
似たようなアノテーション
7
• @Service以外にも、Beanにしてもらえるアノテーションがあ
る
• これらのアノテーションのことを、「ステレオタイプアノテーショ
ン」という。
• コンポーネントスキャン※がどこかで指示されてる必要がある
種類 付加機能 使い分け
@Controller Spring MVCと連携できる Controllerの役割のクラスに付
与する
@Service なし Service(業務ロジック)の役割の
クラスに付与
@Repository スローした例外を、Springの
例外に変換してくれる
データアクセス(Daoなど)の役割
のクラスに付与
@Component なし 上記以外の役割のクラスに付与
※コンポーネントスキャン:ステレオタイプアノテーションが付いたクラスを探すSpringの
行為。指定されたパッケージ配下を探す。
≪参考≫Beanにしてもらうための手段
• ステレオタイプアノテーション
• JavaConfig
• XML
8
DIコンテナ
FooServiceオブジェクト
結果は同じ!!
「@Autowired」の意味
• よくある「誤った」認識
– FooDaoのオブジェクト(依存するオブジェクト)を、
@Autowiredで「生成」している。
9
FooServiceオブジェクト
FooDaoオブジェクトfooDao new演算子
@Autowired
「@Autowired」の「正しい」認識
• 「他のBeanをインジェクションしてほしい」と宣言す
るアノテーション
• インジェクション:依存するオブジェクトを、自分で作
ったり探したりせずに、誰か(Springの場合だとDIコ
ンテナ)に渡してもらうこと。
10
FooServiceオブジェクト
FooDaoオブジェクト
fooDao
DIコンテナ
オブジェクトを生成している訳
ではない
@Autowired
複数のBeanにインジェクションも可能
11
BarServiceオブジェクト
fooDao
FooServiceオブジェクト
fooDao
FooDaoオブジェクトDIコンテナ
@Autowired
@Autowired
「@Transactional」の意味
• 「このクラスの持ってるメソッド(private以外)で、DBのト
ランザクション制御をしてほしい」と宣言するアノテー
ション※
12
FooServiceオブジェクト
abcメソッド()FooControllerオブジェクト
DB
begin commit /rollback
※個別のメソッドに@Transactionalを付けることも可能
トランザクション制御の仕組み
• SpringのAOPによってトランザクション制御を行う「Proxy」が自動生成されている
• 「Proxy」は、Serviceと同じメソッドを持っている。
• Controllerにインジェクションされてるオブジェクトは、Proxyである。
13
abc
Controller Proxy Service Dao データアクセ
ス機能
abc
SQL
ThreadLocal
Connection
connect、begin
commit/rollback、close
SQL
ここまでのまとめ
• @Service
– 「オブジェクトを作ってDIコンテナで管理してほしい」とお
願いするアノテーション
– 「ステレオタイプアノテーション」の1つ
• @Autowired
– 「依存するオブジェクトをインジェクションしてほしい」とお
願いするするアノテーション
• @Transactional
– 「DBのトランザクション制御をしてほしい」と宣言するアノ
テーション
– 内部では「Proxy」が生成されている。
14
おかしな作りになっていません
か?
15
スレッドセーフになっていますか?
• スレッドセーフとは?
– 複数のスレッドで同時に処理されても安全なこと。
• Bean(Springが管理するオブジェクト)は原則シングルトン※1
なので、複数のスレッドが1つのオブジェクトのメソッドを呼び
出す。
16
スレッドB
オブジェクト
abcメソッド
※1:シングルトン
1つのオブジェクトを使いまわすこと
複数のスレッドが1つのオ
ブジェクトのメソッドを呼び
出す
Webアプリケーションの場合
• 1つのController・Service・Daoのオブジェクトが複数のリク
エストの処理を同時に行う。
• リクエストごとに別のオブジェクトが使われる訳ではない
17
FooController FooService FooDaoリクエストB
リクエストA
リクエストB
リクエストC
FooController FooService FooDao
FooController FooService FooDao
FooController FooService FooDao
スレッドセーフではないアプリ
• スレッド個別のデータをフィールド(インスタンス変数)
で保持している
18
スレッドB
FooService
totalPrice
calclateOrderPriceメソッド
?
【NGな作り】
スレッドセーフなオブジェクトにするには?
• スレッド個別のデータをローカル変数で保持すればよい。
– ローカル変数は、メソッドが呼び出される度に個別の変数になる
19
スレッドB
FooService
calclateOrderPriceメソッド
【OKな作り】
≪補足≫Beanにすべきではないクラス
• SpringのBeanはシングルトンが原則なので、処理
毎にフィールドの値が変わるような役割のオブジェク
トはBeanにしない。
– EntityやDTOなど
20
処理の度にオブジェクトが作られて
固有のデータがフィールドに格納される
トランザクションの入れ子はうまくできていますか?
• トランザクションの入れ子とは?
– ここでは、あるトランザクションの中で、別のトランザクショ
ンを制御すること
– 利用シーン
• バッチ処理で、切りのいい単位でコミットする
• 採番テーブルの更新
• アクセス監査テーブルの更新 21
abcメソッド {
処理A
処理B
処理C
処理D
}
外側のトランザクション
内側のトランザクション
やってしまいがちな誤り
• メソッドを分けて@Transactionalを付与する
22
@Transactional
abcメソッド {
処理A
defメソッド();
処理D
}
@Transactional(propagation
=Propagation.REQUIRES_NEW)
defメソッド{
処理B
処理C
}
しかし、defメソッドを呼び出しても、
別のトランザクションは開始されない。
(外側のトランザクションに含まれてしまう)
理由は次のページ
Proxyが処理を経由できない
• 内部メソッドの呼び出しは、Proxyの処理を経由しな
いため、新たなトランザクションは開始されない。
23
abc
Controller Proxy Service
abc
connect、begin
commit/rollback、close
Proxyを経由せずに
呼び出している
def
解決策(1/2)
• 解決策A:クラスを分割する
24
@Transactional
abcメソッド {
処理A
barService.defメソッド();
処理D
}
@Transactional(propagation
=Propagation.REQUIRES_NEW)
defメソッド{
処理B
処理C
}
FooService BarService
abc
Controller ProxyA FooService
abc
connect、begin
commit/rollback、close
def
ProxyB BarService
def
connect、begin
commit/rollback、close
解決策(2/2)
• 解決策B:TransactionTemplateクラスを利用して、
メソッドの中で明示的にトランザクションを開始・終
了する
– 詳細はSpringのマニュアルを参照。
25
@Transactional
abcメソッド {
処理A
new TransactionTemplate(...).execute(
new TransactionCallback(...) {
処理B
処理C
}
);
処理D
}
プログラムのイメージ
≪補足≫トランザクション制御のログを出力する方法
• トランザクションマネージャーの実装クラスのロガーをDEBUGレベルに
する
– トランザクションマネージャー:下位レベルのAPIを使って実際にトランザクシ
ョンの指示を出すオブジェクト
– データアクセス技術によって実装クラスが異なる
• DataSourceTransactionManager
• JpaTransactionManager
など
– ログの内容
26
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
【SpringBootのapplication.propertiesで指定した例】
トレースログを明示的に出力していませんか?
• ソースコードの可読性が落ちる。
• 異常終了時のログを出力したいがためにtry・catch・
throwを記述している。
– throwを忘れて例外を握りつぶすバグの可能性もでてくる
27
• SpringのAOPを利用する
解決策
28
abc
Controller Proxy Service
abc
ログ出力
ログ出力
ワイルドカードを使って
複数のメソッドに適用
可読性が上がり、
バグも潜みにくい
ControllerやDaoに
適用することも可能
ここまでのまとめ
• Beanはスレッドセーフにする
– フィールドにスレッド個別のデータを保持しない
• トランザクションの入れ子に注意
– 内部メソッドを呼び出してもトランザクションは開始されな
い
• クラスを分ける or TransactionTemplateを利用
• トレースログを個別のクラスで出力しない
– AOPを利用する。
29
おすすめの資料
• SlideShare「今さら聞けないDIとSpring」
– https://www.slideshare.net/KouheiToki/dispring
• SlideShare「これから始めるSpringのwebアプリケーション」
– https://www.slideshare.net/KouheiToki/springweb-82685380
• SlideShare「Springを何となく使ってる人が抑えるべきポイント」
– https://www.slideshare.net/KouheiToki/spring-66768974
30
31
ご清聴ありがとうございました
32
ライセンスについて
• JSUGマスコットアイコン(本スライド左下)が残されている場合に限り、本作品(またそれを元にした派生
作品)の複製・頒布・表示・上演を認めます。
• 非商用目的に限り、本作品(またそれを元にした派生作品)の複製・頒布・表示・上演を認めます。
• 本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。

これからSpringを使う開発者が知っておくべきこと