今さら人には聞けない AOP 入門 2006.11.12 エスエムジー株式会社小森裕介 (komori@smg.co.jp) 1
はじめに えっ!?AOP って もう 今さら聞けない の? そんなことはない! と思います でも AOP が開発の中で一般的になりつつあるのもまた事実 そろそろ 知らない って言えなくなってきたアナタに AOPの基礎を50 分で伝授します! 2
はじめまして! 名前 : 小森裕介 Blog:http://d.hatena.ne.jp/y-komori/ 所属 : エスエムジー株式会社 (http://www.smg.co.jp) 主な仕事 : Javaによる集中監視制御システム設計 開発 Webアプリケーションシステムの設計 開発 教育 各種執筆活動 日経ソフトウェア とことん作って覚える! Java 入門 連載中 なぜ あなたはJavaでオブジェクト指向開発ができないのか Seasar2 とのかかわり S2Container コミッタ S2JMS コミッタ S2JFace コミッタ 3
具体例で考えてみよう! AOP がなければ何が困る? ジャンケンプログラムにロギング処理を追加したい! 4
Log4j でロギング処理を追加しよう! ジャンケンの手を決めるメソッドへロギング処理を追加 <<interface>> Player 人間の入力した手を読み取るクラス <<abstract>> AbstractPlayer コンピュータの手を決めるクラス HumanPlayer ComputerPlayer ロギング処理を追加 ロギング処理を追加 5
追加されたロギング処理 Before public class ComputerPlayer extends AbstractPlayer { private Tactics tactics; public int showhand() { int hand = tactics.readtactics(); return hand; public void settactics(tactics tactics) { this.tactics = tactics; 追加されたロギング処理 After public class ComputerPlayer extends AbstractPlayer { private Tactics tactics; private static final Logger logger = Logger.getLogger(ComputerPlayer.class); public int showhand() { int hand = tactics.readtactics(); loghand(hand); return hand; public void settactics(tactics tactics) { this.tactics = tactics; private void loghand(int hand) { switch (hand) { case Janken.STONE: logger.debug(" コンピュータ : グー "); break; case Janken.SCISSORS: logger.debug(" コンピュータ : チョキ "); break; case Janken.PAPER: logger.debug(" コンピュータ : パー "); break; 6
ロギング処理を共通化したい! ロギング処理に重複部分があるのでなんとかしたい <<interface>> Player public class ComputerPlayer extends AbstractPlayer { private Tactics tactics; HumanPlayer <<abstract>> AbstractPlayer ComputerPlayer @Override public int showhand() { int hand = tactics.readtactics(); HandLogger.logHand(PlayerType.COMPUTER, hand); return hand; ロギングモジュールの呼び出し部分は除去できない HandLogger +loghand() 共通処理を別クラスに分離 7
呼び出し部分が残ると何が問題か ロギングモジュールの呼び出し部が依存部分として残ってしまうロギング処理を削除しようとすると呼び出し元にも影響が出る HumanPlayer HumanPlayer HandLogger HandLogger ComputerPlayer +loghand() ComputerPlayer +loghand() 単独での再利用が不可能! モジュール側の再利用は簡単 8
似たようなクラスを作成するときの問題 共通モジュールの呼び出しは開発者に依存してしまう HumanPlayer ComputerPlayer HandLogger +loghand() xxxxplayer 新しいクラスを作るとき開発者が呼び出しを忘れてしまう 9
非機能要件はモジュール単独分離が難しい 非機能要件に関する処理は 全機能に影響するため モジュールとして分離しにくいロギング処理も非機能要件の一つ非機能要件 システムの本来の機能とは関係ないが 信頼性や保守性 使いやすさを向上させるための要件 機能要件 A 機能要件 B 機能要件 C 機能要件 D 非機能要件 各機能に共通する処理をモジュール化する方法はないの? 10
アスペクト指向は関心事の分離から システムを 2 種類の要件に分け 横断的関心事を分離するのがアスペクト指向の考え方 ビジネス A ビジネス B ビジネス C ビジネス D セキュリティ ( アクセス制御 情報隠蔽 承認 完全性 ) 中心的関心事 (Core (Core concern) concern) 信頼性 ( バックアップ 分散 冗長性の確保 ) 運用情報 ( 監視 稼働状況 負荷管理 障害状況 ) マイグレーション ( 配備 設定 保守 開発計画 ) 横断的関心事 (Cross (Cross cutting cutting concern) concern) 出典 Seasar2 で学ぶ DI と AOP (arton 著 技術評論社 )p20 11
ジョインポイントとアドバイス アスペクト指向では 機能の中にジョインポイントを定義し アドバイスをウィービングする アドバイス (Advice) (Advice) 追加される処理追加される処理 ビジネスA ビジネスA ビジネスB ビジネスB ビジネスC ビジネスC ビジネスD ビジネスD セキュリティ ( アクセス制御 情報隠蔽 承認 完全性 ) セキュリティ ( アクセス制御 情報隠蔽 承認 完全性 ) 信頼性 ( バックアップ 分散 冗長性の確保 ) 信頼性 ( バックアップ 分散 冗長性の確保 ) 運用情報 ( 監視 稼働状況 負荷管理 障害状況 ) 運用情報 ( 監視 稼働状況 負荷管理 障害状況 ) マイグレーション ( 配備 設定 保守 開発計画 ) マイグレーション ( 配備 設定 保守 開発計画 ) ジョインポイント (Joinpoint) (Joinpoint) 処理を追加する場所処理を追加する場所 ウィービング (Weaving) (Weaving) 処理を追加すること処理を追加すること 12
どのようなジョインポイントがあるか? ジョインポイント (Joinpoint) はアドバイスを挿入可能なプログラム上の位置 1 呼び出されたメソッドの開始点 2 呼び出されたメソッドの終了点 3 メソッドの呼び出し地点 4 フィールドの参照 更新地点 5 クラスの生成地点 private private int int x; x; methoda methoda () () { { 3methodB(); methodb(); 4x x = = x x + + 1; 1; 3methodC(); 5new new XXX(); XXX(); methodb methodb () () { { 1 2 methodc methodc () () { { 13
ジョインポイントとアドバイスを結びつけるポイントカット どのジョインポイントにどのアドバイスを結びつけるかはポイントカット (Pointcut) で指定する どのジョインポイントをどのアドバイスに結びどのアドバイスに結びつけるかを指定するつけるかを指定する アドバイス ポイントカット ジョインポイント プログラム実行の流れ ( スレッド ) 出典 アスペクト入門 ( 千葉滋著 技術評論社 )p47 14
実際にどうやって実現するの? AOP(Aspect-Oriented Programming: アスペクト指向プログラミング ) はどうやって実現するの? AOP を利用するには 2 種類の方法がある 1. 専用コンパイラを利用する方法 2.DI コンテナを利用する方法 15
専用コンパイラを利用する方法 AspectJ による AOP の実現 Java ソースコード 通常の通常のJava Javaクラス コンパイルと同時にコンパイルと同時にウィービングを行ってウィービングを行ってクラスファイルを出力する Javaクラスファイル アスペクト (Aspect) アドバイスとポイントカットを記述したものを記述したもの AspectJ コンパイラ public aspect HandLogger { pointcut loggedmethods(): execution(void HumanPlayer.showHand()) execution(void ComputerPlayer.showHand()); after():loggedmethods() { // ログ出力処理 ポイントカットポイントカット アドバイスアドバイス 16
DI コンテナを利用する方法 DI コンテナ (Seasar2) による AOP の実現 Java ソースコード アドバイス (Java ソースコード ) Java コンパイラ Java コンパイラ 実行時に内部で実行時に内部でウィービングを行うウィービングを行う DI コンテナ (Seasar2) Dicon ファイル アスペクトを記述アスペクトを記述 17
S2AOP を体感してみよう! アスペクトでジャンケン のロギング処理を S2AOP で書き直す <<interface>> Player <<interface>> MethodInterceptor <<abstract>> AbstractPlayer <<abstract>> AbstractInterceptor HumanPlayer ComputerPlayer HandInterceptor +invoke() Seasar2 に含まれるクラス ロギングに関する記述はナシ! Dicon ファイル CoreConcern と CrossCuttingConcern を分離できた! アスペクトを記述 18
S2AOP での Dicon ファイルの記述 S2AOP では XML(Dicon ファイル ) にアスペクトを記述する ポイントカットの指定ポイントカットの指定 (( アドバイスを追加するメソッド )) <components> <component name="player" class="org.seasar.jface.example.janken.impl.humanplayer"> <aspect pointcut="showhand">handinterceptor</aspect> </component> <component name="computer" class="org.seasar.jface.example.janken.impl.computerplayer"> <aspect pointcut="showhand">handinterceptor</aspect> </component> <component name="handinterceptor" class="org.seasar.jface.example.janken.interceptors.handinterceptor" /> </components> コンポーネントとして登録されたインターセプターインターセプター (( アドバイスアドバイス )) コンポーネントに対するアスペクトアスペクト ウィービング対象のウィービング対象のコンポーネントコンポーネント 19
S2AOP の仕組み ちょっとだけ S2AOP の仕組みを紹介しましょう 2 オーバーライド HumanPlayer HumanPlayer$S2AOP public Object invoke(methodinvocation invocation) throws Throwable { // 元のメソッド実行前に行いたい処理 invocation.proceed(); // 元のメソッド実行後に行いたい処理 Seasar Seasarが Javassist Javassist を利用してを利用して実行中にサブクラスを自動生成 1 3 HandInterceptor +invoke() More Info! Javassist: 国産のバイトコートエンジニアリングライブラリ http://www.csg.is.titech.ac.jp/~chiba/javassist/ 20
Seasar2 の提供するインターセプター TraceInterceptor メソッド呼び出しのトレースを自動出力 ThrowsInterceptor 例外のキャッチを一括処理 ToStringInterceptor tostring() メソッドを自動処理 MockInterceptor モックを使ったテストを支援 DelegateInterceptor メソッド呼び出しを別コンポーネントへ委譲 SyncInterceptor メソッド呼び出しを同期化 More Info! http://s2container.seasar.org/ja/aop.html 21
S2DAO とAOP S2DAO も AOP によって実現されている インターフェースしか用意していないのに処理が行われるのはどうして?? <<interface>> xxxdao +insert() +update() +delete() xxxdao$s2aop +insert() +update() +delete() ユーザが作成したユーザが作成したインターフェースインターフェース S2AOP S2AOPで自動生成された実装クラス実装クラス DaoInterceptor S2Dao S2Daoの提供するインターセプタがインターセプタがDao Daoとしての機能を提供機能を提供 アスペクト指向でソースコード生成を伴わないスマートな DAO が可能に! More Info! http://s2dao.seasar.org/ja/s2dao.html 22
既存のクラスを拡張するインタータイプ宣言 インタータイプ宣言とは 既存のクラスの静的構造を変更するアスペクトの機能 Seasar2.4 で提供される新機能 InterType 既存のクラスに対してメソッドやフィールドを自由に追加可能 -x -y Point +calcarea(); Point -x -y -color +calcarea(); +setcolor(); プログラム実行中にプログラム実行中に追加追加! ソースコード自動生成に替わる手段として利用可能! More Info! http://s2container.seasar.org/ja/aop.html#originalintertype 23
アスペクト指向をさらに知りたい方へ アスペクト指向入門 -Java オブジェクト指向から AspectJ プログラミングへ 著 : 千葉滋 価格 : 2,480+ 税 出版社 : 技術評論社 ISBN:4-7741-2581-4 24
ご質問について スピーカーブースで お待ちしています 気軽にお越しください 25
ご静聴 ありがとうございました 26