• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Spring3.1概要 AOP & MVC
 

Spring3.1概要 AOP & MVC

on

  • 8,858 views

JSUG 第2回 初心者向け勉強会

JSUG 第2回 初心者向け勉強会

Statistics

Views

Total Views
8,858
Views on SlideShare
7,895
Embed Views
963

Actions

Likes
14
Downloads
132
Comments
0

10 Embeds 963

http://d.hatena.ne.jp 916
http://webcache.googleusercontent.com 26
http://hatenatunnel.appspot.com 12
http://dhatenane.greatbabyfood.com 2
http://cache.yahoofs.jp 2
http://translate.googleusercontent.com 1
http://morizo999.hatenablog.com 1
http://dunjiii.com 1
http://a0.twimg.com 1
http://cc.bingj.com 1
More...

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Spring3.1概要 AOP & MVC Spring3.1概要 AOP & MVC Presentation Transcript

    • AOPとMVCfor Beginner 2012/2/20
    • 問題•  インスタンス変数に宣言したのと同じ型 のインスタンスをインジェクションして もらうためのアノテーションは?•  インスタンス化され、インジェクション されるために、クラス宣言の前に書くア ノテーションは?•  結局、DIコンテナって何のためにあるの か? 2
    • 前回の続き•  SpringのDIを使って、部品化はできた (気がする)•  でも・・・ ‒  ログなどの共通処理部分が部品に残っている ‒  例外処理も部品化を壊している•  それに・・・ ‒  トランザクション管理は面倒だし、難しい 3
    • Springの説明の前に・・・AOPの基本 4
    • AOPを使ってもっと部品化する•  AOPを使えば処理を後からクラスに追加できる ‒  例:トレースログを追加する find()を呼ぶ DaoImpl ServiceImpl find() Dao public class DaoImpl extends Dao{ >java ・・・ ・・・ 16:00:01 *Start* find() DaoImpl public List find() { 16:00:02 *End* find() DaoImpl List list = select(); 17:02:12 *Start* find() DaoImpl return list; 17:02:13 *End* find() DaoImpl } } 実行結果 5
    • AOPをもう少し正しくAdviceを追加できるときが Joinpointとなる Joinpointを条件で絞り込む フィルター Aspect Pointcut Advice ソースコード Pointcut Advice Joinpoint 追加したい処理 6
    • Joinpoint•  Adviceを追加できるとき(ポイント)•  AOPの仕様であり、実装者は変更できない•  Joinpointの仕様例 ‒  メソッドの開始時、終了時 ‒  プロパティが利用されたとき DaoImpl add() delete() find() update() Joinpoint 例:メソッドの開始時や終了時 7
    • Pointcut•  Joinpointに達した命令を、Adviceまで到 達させるか否かフィルタリングするフィ ルター Joinpointを条件「add*」で絞り込む DaoImpl Pointcut add() delete() AdviceServiceImpl find() Dao update() Joinpoint 追加したい処理 8
    • Befor Advice•  Joinpointの前で実行される メソッドの実行前に割り込む method() Before Advice Client Servant return Exception 9
    • After Advice•  Joinpointの後で実行される method() Client Servant After Advice return Exception メソッドの実行後に割り込む 10
    • After Returning Advice•  Joinpointが正常終了した後に実行される method() return Client After Returning Advice Servant Exception メソッドの実行後に正常終了時 に割り込む 11
    • Around Advice•  Joinpointの前後で実行される method() method() Client Around Advice return Servant Exception メソッドの実行前と実行後、例 外発生時にも割り込む 12
    • Throw Advice•  Joinpointで例外が発生した時に実行される method() Client Servant return Throw Advice Exception 例外発生時に割り 込む 13
    • 問題•  メソッドの開始と終了のログをとりた かったら?•  メソッドが正常終了したログをとりた かったら?•  メソッドが異常終了したときのログをと りたかったら? 14
    • AOPの仕組み例•  Proxyベースの場合、ProxyオブジェクトはSpringが自動生成する :Bean :Advice 処理の 処理の依頼 依頼 Adviceの呼び出し :Client :Proxy :Spring Interface 自動生成 Bean定義 Pointcutの参照 ファイル 1-15
    • AOPの主な利用方法•  各クラスに記述されている同一の処理を抜き出し、ひと まとめにして、既存のクラスに後から追加する ‒  ライブラリとの違い •  ライブラリは呼び出さないといけない •  AOPは勝手に追加される•  追加すると便利な処理 ‒  トランザクション管理 •  トランザクション管理は難しいくプログラマに任せられない ‒  ログ管理 •  メソッドの開始と終了のトレースログが正しく出力されない ‒  誰もフォーマットを守らない ‒  トレースログを追加し忘れる ‒  例外管理 •  処理の途中でExceptionが握りつぶされてしまう ‒  Exceptionを実行時例外にする 16
    • AOPでやらない方が良いこと•  個別の処理(特定業務の処理、デバッ グ) ‒  プログラマが個別にAOPをいれるのは不可 •  そこで何をやっているのかが分からなくなる•  業務アプリのプログラマではなく、基盤 チームとかが使う技術!? 17
    • SpringのAOP•  定義ファイルの利用 ‒  Spring1.x系では基本•  アノテーションの利用 ‒  Spring2.x系以降、アノテーションの利用が増 えている(大規模開発や大手SI便だでは定義 ファイルの利用が多い) 18
    • 問題 書いてないけど?•  例えば ‒  アノテーション •  メリット:定義ファイルの管理が不要 •  デメリット:プログラマにアノテーションを意識 ‒  とか、定義ファイルのメリットとかデメリット 19
    • アノテーションを使ったAOP 20
    • アスペクトの例•  アノテーションの利用@Aspectpublic class AspectMessage { @After("execution(* exMethod())") public void hoge() { // メソッド終了後に動くAdvice System.out.println("after called"); }} 21
    • アノテーション一覧•  アノテーションを利用したAOP ‒  Bean定義ファイルの記述が簡潔になる ‒  ソースコードに記述することで管理が煩雑 アノテーション 説明 @Aspect Adviceとなるクラスを指定するアノテーション @Around Around Adviceとなるメソッドを指定するアノテーション @Before Before Adviceとなるメソッドを指定するアノテーション @After After Adviceとなるメソッドを指定するアノテーション @AfterReturning After Returning Adviceとなるメソッドを指定するアノテーション@AfterThrowing After Throwing Adviceとなるメソッドを指定するアノテーション 22
    • アドバイス詳細(1)•  Before, After ‒  @After( Primitiveポイントカット ) •  メソッド名は任意、メソッドのパラメータと戻り値はなしでも可能。 メソッド内で、アスペクト対象となっているメソッド名やパラメー タ,戻り値などの取得をする場合は、パラメータにJoinPoint •  メソッド内で、アスペクト対象となっているメソッドを呼び出す必 要はない @Before(“execution(* exMethod())”) public void hoge() { ・・・ } 23
    • アドバイス詳細(2)•  Around ‒  @Around( Primitiveポイントカット ) •  メソッド名は任意、メソッドのパラメータには必 ずProceedingJoinPointが必要、戻り値はアスペ クト対象のメソッドにあわせる •  メソッド内で、アスペクト対象となっているメ ソッドを呼び出す必要がある ‒  ProceedingJoinPoint#proceed()メソッド »  Object proceed() throws Trowable •  メソッド内で、アスペクト対象となっているメ ソッド名やメソッドのパラメータの取得は ProceedingJoinPointを介しておこなう 24
    • アドバイス詳細(3)•  Around ‒  戻り値はアスペクト対象にあわせるpublic String getMessage() { return “hello!”; アスペクト対象のメソッド } @Around(“execution(* getMessage())”) public String fuga(ProceedingJoinPoint pjp) throws Throwable {; String msg = (String)pjp.proceed(); return msg; } 25
    • アドバイス詳細(4)public int getFigure() { return 100; アスペクト対象のメソッド } @Around(“execution(* getFigure())”) public int fuga(ProceedingJoinPoint pjp) throws Throwable {; Integer figure= (Integer)pjp.proceed(); return figure.intValue(); } 26
    • アドバイス詳細(5)•  Around ‒  ProceedingJoinPointの使い方 メソッド名の取得 Signature sig = pjp.getSignature(); System.out.println("Sig: " + sig.getName()); パラメータの取得(最初のパラメータ) Object[] os = pjp.getArgs(); System.out.println("Args: " + os[0]); 27
    • 問題∼重複してたらどうなる?@Around(“execution(* add())”)public int hoge (ProceedingJoinPoint pjp) throws Throwable {; Integer figure= (Integer)pjp.proceed(); return figure.intValue(); これと同じhogehogeメソッド} が存在したら?private int x;public int add(int i) { x = x + i; アスペクト対象のメソッド return x; } 28
    • アドバイス詳細(6)•  AfterReturning ‒  @AfterReturning(value= Primitiveポイント カット , returnig = 戻り値の変数名 ) •  メソッド名は任意、メソッドのパラメータはアス ペクト対象となっているメソッドの戻り型とアノ テーションのretuning属性で指定した変数名 •  メソッド内で、アスペクト対象となっているメ ソッドを呼び出す必要はない 29
    • アドバイス詳細(7)public String exMethod() { return “hello!”; アスペクト対象のメソッド } @AfterReturning(value=“execution(* exMethod())”, returning=“ret”) public String hoge(String ret) { System.out.println(“Return: “ + ret); } 30
    • アドバイス詳細(8)•  AfterThrowing ‒  @AfterReturning(value= Primitiveポイント カット , throwing = 例外の変数名 ) •  メソッド名は任意、メソッドのパラメータはアス ペクト対象となっているメソッドの戻り型とアノ テーションのthrowing属性で指定した変数名 •  メソッド内で、アスペクト対象となっているメ ソッドを呼び出す必要がない 31
    • アドバイス詳細(9) @AfterThrowing(value=“execution(* exMethod())”,                              throwing=“ex”) public String foo(HogeException ex) { System.out.println(“Exception Msg: “ + ex.getMessage()); } 32
    • アドバイス詳細(10)•  AfterThrowing @AfterThrowing(value=“execution(* exMethod())”, throwing=“ex”) public String foo(Throwable ex) { System.out.println(“Exception Msg: “ + ex.getMessage()); } @AfterThrowing(value=“execution(* exMethod()”, throwing=“ex”) public String var(HogeException ex) { System.out.println(“Exception Msg: “ + ex.getMessage()); } @AfterThrowing(value=“execution(* exMethod()”, throwing=“ex”) public String hoge(Exception ex) { System.out.println(“Exception Msg: “ + ex.getMessage()); } 33
    • 問題∼どのアドバイス?•  前ページの実装があったとき、どこかの プログラムがExceptionを投げてよこしま した•  どのアドバイスが動くでしょう? 34
    • Primitiveポイントカット Primitive ポイントカット 概要 execution 呼出先の「メソッド」、「コンストラクタ」を指定する。 within 呼出元の「クラス」を指定する。
 withinをPointcutに指定すると、指定されたクラスから呼出される、メソッ ド等が選択されることになる。
 対象は、指定されたクラスで宣言されたメソッドに限定され、指定された クラスの親クラスで宣言されたメソッド内は対象外となる。 this 呼出元の「クラス」を指定する。
 thisをPointcutに指定すると、指定されたクラスから呼出される、メソッド 等が選択されることになる。
 withinとは、親クラスで定義されたメソッドの呼出しも対象とする点が異な る。 target 呼出先の「クラス」を指定する。
 ただし、呼出先のstaticフィールドは対象外となる。 args 呼出先「メソッド」の引数の型を指定する。 ※Primitiveポイントカット:あらかじめ用意されているポイントカットのこと 35
    • executionの基本構文•  execution(メソッドの修飾子△メソッドの戻り値型△ パッケージ.クラスまたはインタフェース.メソッド名 (仮引数の型|,仮引数の型…|) △throws 例外)•  「メソッドの修飾子(publicやprivateは省略可能)」や 「throws△例外」は省略することが可能•  メソッドの戻り値型、パッケージやクラス名、インタ フェース名にはワイルドカード(*)の利用が可能•  仮引数に(..)を記述すると任意の個数の引数と一致さ せることが可能 36
    • ポイントカットで利用できる論理演算子論理 説明 演算子 || または 論理和を意味する論理演算子 or 例) execution(* *..AopExBean.exMethod()) or execution(* *..AopExBeanParent.exMethod())  →AopExBeanのメソッドexMethodまたはAopExBeanParentのメソッドexMethodを指定 && また 論理積を意味する論理演算子 は and 例) execution(* *..AopExBean.exMethod()) && execution(* *..AopExBeanParent.exMethod())  →AopExBeanのメソッドexMethodまたはAopExBeanParentのメソッドexMethodを指定! または 否定を意味する論理演算子。 not 例) execution(* exMethod()) and not execution(* *..AopExBeanParent.*())  →AopExBeanParent以外のクラス(インタフェース)のメソッドexMethodを指定 37
    • コーディング例(1)@Aspectpublic class AspectMessage { @After("execution(* exMethod())") public void after() { // メソッド終了後に動作するAdvice System.out.println("after called"); } @Before("execution(* exMethod())") public void before() { // メソッド開始時に動作するAdvice System.out.println("before called"); } 38
    • コーディング例(2)@Around("execution(* exMethod())") public void around(ProceedingJoinPoint pjp) throws Throwable { // メソッド呼出の前後に動作するAdvice System.out.println("pre proceed"); pjp.proceed(); System.out.println("post proceed"); } 39
    • コーディング例(3) @AfterReturning(value="execution(* exMethod())", returning="ret") public void afterReturning(String ret) { // メソッド呼出が例外の送出なしに終了した際に動作するAdvice System.out.println("after returning called"); System.out.println("return value = " + ret); } @AfterThrowing(value="execution(* exMethod())", throwing="ex") public void afterThrowing(Throwable ex) { // メソッド呼出が例外の送出なしに終了した際に動作するAdvice System.out.println("after throwing called"); System.out.println("exception value = " + ex.toString()); }} 40
    • 定義ファイル<?xml version="1.0" encoding="UTF-8"?><beans xmlns=http://www.springframework.org/schema/beans xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:context=http://www.springframework.org/schema/context xmlns:aop=http://www.springframework.org/schema/aop xsi:schemaLocation=” http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">… <aop:aspectj-autoproxy/>…</beans> 41
    • 定義ファイルには書かないでしょ!?AOP 42
    • それでも書きたい定義ファイル(1) ・・・<aop:config> <aop:aspect id="myAspect" ref="aspectMessage"> <aop:pointcut id="fuga" expression="execution(* getMessage())"/> <aop:before pointcut-ref="fuga" method="foo"/> <aop:after pointcut-ref="fuga" method="var"/> <aop:around pointcut-ref="fuga" method="hoge"/> </aop:aspect></aop:config> 43
    • それでも書きたい定義ファイル(2)<aop:config> <aop:aspect id="myAspect2" ref="aspectMessage2"> <aop:pointcut id="fuga2" expression="execution(* getMessage())"/> <aop:after pointcut-ref="fuga2" method="hoge"/> </aop:aspect></aop:config>・・・<bean id="aspectMessage" class="sample.aop.AspectMsg" /><bean id="aspectMessage2" class="sample.aop.AspectMsg2" />・・・ 44
    • コーディング例public class AspectMessage { public void after() { // メソッド終了後に動作するAdvice System.out.println("after called"); } public void before() { // メソッド開始時に動作するAdvice System.out.println("before called"); }・・・以下省略 45
    • 問題 消すことできる?<aop:config> <aop:aspect id="myAspect2" ref="aspectMessage2"> <aop:pointcut id="fuga2" expression="execution(* getMessage())"/> beanを定義している2行 <aop:after pointcut-ref="fuga2" method="hoge"/> </aop:aspect></aop:config>・・・<bean id="aspectMessage2" class="sample.aop.AspectMsg2" /><bean id="aspectMessage2" class="sample.aop.AspectMsg" />・・・ 46
    • 今までの知識を使ってWebアプリケーションを改善するDIとAOPのまとめアーキテクチャ・リファクタリング 47
    • アーキテクチャ・リファクタリング(1/5) Employee Employee FindAction MySql RDB Service Dao ブラウザ •  表示と永続化のフレームワーク導入済•  インタフェース未使用(もちろんDI,AOPも) ‒  チーム開発がしずらい ‒  変更、機能拡張、テストが容易ではない•  連続性も阻害 ‒  Conecctionの引き回し、検査時例外の伝搬 48
    • アーキテクチャ・リファクタリング(2/5)インタフェースの導入 Employee Employee Service Dao <<Singleton>> <<Singleton>> FindAction Employee Employee MySQL ServiceImpl DaoMySqlブラウザ RDB 利用 利用 生成 生成 Factory •  メリット ‒  インタフェースを区切りとして、チーム開発がやりやすくなった ‒  変更、機能拡張、テストが容易になった •  デメリット ‒  Factoryを実装する必要がある ‒  クラスはFactoryに依存する 49
    • アーキテクチャ・リファクタリング(3/5)DIの導入 Employee Employee Service Dao Employee Employee FindAction MySQL ServiceImpl DaoMySqlブラウザ RDB 生成 生成 Injection Injection DIコンテナ •  Factoryを実装する必要がない •  クラスはDIコンテナに依存しない 50
    • アーキテクチャ・リファクタリング(4/5) DIコンテナ導入後のソースコード •  トランザクション管理やログ出力、例外処理が残っている Employee ‒  分岐が多いため、テストの量が多くなる ServiceImpl ‒  共通化できる部分が隠蔽できていない ‒  例外処理とConnectionの引き渡しによる連続性の阻害があるpublic class EmployeeServiceImpl" } catch(Exception e) {! implements EmployeeService{" conn.rollback();! @Autowired! ・・・ private EmployeeDao dao;" } finally {! ・・・ conn.close();! public List findAll() throws Exception {! ・・・ if(Log.flag) { System.out.println(“***Start”); }! }! Connection conn = null;" if(Log.flag) { System.out.println(“***End”); }" ・・・ return employeeList;" //EmployeeDao dao ! }" // = (EmployeeDao)Factory.create(KEY);! ・・・ List employeeList = null;" try {" employeeList = dao.findAll(conn);" conn.commit();! 51
    • アーキテクチャ・リファクタリング(5/5) AOP導入後のソースコード •  共通ライブラリを廃止してAOPを導入•  連続性の確保 ‒  トランザクション管理、ログ出力、例外処理はAOPで実現しているため、ソースコード上から はなくなっている ‒  Advice実装されており、なくなっている訳ではない•  ソースコードの記述量が減り、バグの数も低下、開発者の作業も軽減•  テストの容易性も向上 public class EmployeeServiceImpl ! 書くことがなくなりました・・・! implements EmploeeService {! @Autowired! private EmployeeDao dao;!   public List findAll() {! return dao.findAll();! }! ・・・ 52
    • アーキテクチャ・リファクタリングの嘘•  AOPで業務例外(例えば、在庫がなかった時に どうする)は処理できないから、そんなに奇麗 に例外は消えない(多分…)•  だって、AOPを使う基盤チームは業務を知らな い(多分…)。だから、業務例外はAOPで提供 できない•  それに業務例外がAOPになったら、業務プログ ラムが読めない!•  そもそも、業務例外にExceptionを使うのって どうよ(!?)という問題でもある 53
    • かる∼くSpring MVC 54
    • Spring MVCとは•  Spring Frameworkに含まれるWeb MVCフレームワーク ‒  初期のSpring Frameworkの段階から含まれ ている ‒  StrutsやJSFと競合•  特徴 ‒  DIコンテナとの親和性 ‒  きれいな設計 •  インタフェースを使用して部品化 55
    • Spring MVCのControllerDIの導入 Employee Employee Service Dao Employee Employee XxxController MySQL ServiceImpl DaoMySqlブラウザ RDB 生成 生成 生成 Injection Injection DIコンテナ 56
    • 難しいと評判(?)のSpring MVC•  Spring1.xのSpring MVC ‒  設定が難しい ‒  作り方がよくわからない ‒  日本語の情報が少ない ‒  そもそも知らない 57
    • 簡単になったSpring MVC•  Spring 3.xのSpring MVC ‒  Springの新機能を導入 •  アノテーションにより設定がシンプルになり、わ かりやすくなった •  component-scanにより設定ファイルが最低限で すむようになった ‒  あいかわらず日本語の情報は少ない •  英語のマニュアルを読みましょう 58
    • デモ•  STS(SpringSource Tool Suite)で作成 1.  メニューからNew->Project 2.  SpringSource Tool Suite->Spring Template Projectを選択 3.  Spring MVC Projectを選択 59
    • 動作概要 <<controller>> Model HomeController "serverTime" Date home() ③ (現在の日時) Dispatcherブラウザ ① Servlet (ほか色々) <<jsp>> /WEB-INF/views/home.jsp ⑥ 60
    • HomeController DIコンテナにより 自動で読み込まれる (component-scan) HTTPメソッドがGETで@Controller 「/」へアクセスした際にpublic class HomeController { 実行される @RequestMapping(value = "/", method = GET) public String home(Model model) { Viewに渡したいオブジェクトを Date date = new Date(); 設定する model.addAttribute("serverTime", date); return "home"; } View名をreturnする } ※少し手を加えシンプルにしています 61
    • home.jsp<html><head> <title>Home</title> Modelに設定したオブジェクトは</head> 自動的にHttpServletRequestに 設定されている <body><h1>Hello world! </h1><p>The time on the server is ${serverTime}.</p></body></html> ※少し手を加えシンプルにしています 62
    • @RequestMapping色々// 一つのメソッドに複数のURLを割り当て@RequestMapping({"/", "/home"})public String home() { ・・・// 一つのURLでHTTPメソッドごとにメソッドを切り分け@RequestMapping(value="/foo", method=GET)public String doGetMethod() { ・・・@RequestMapping(value="/foo", method=POST)public String doPostMethod() { ・・・ 63
    • 引数色々①// リクエストパラメータを取得(「/person?id=10」などでアクセス)@RequestMapping(value = "/person", method=GET)public String showPerson1 ( @RequestParam("id") int id, Model model) { Person person = findById(id); model.addAttribute("person", person); ・・・// URLの値を取得(「/person/10」などでアクセス)@RequestMapping(value = "/person/{id}", method=GET)public String showPerson2( @PathVariable("id") int id, Model model) { Person person = findById(id); model.addAttribute("person", person); ・・・ 64
    • 引数色々②// 画面からの入力をマッピング(formからデータを送信)@RequestMapping(value = "/person", method = POST)public String registerPerson(@ModelAttribute Person person) { register(person); ・・・// ほかにも色々@RequestMapping("/foo")public String foo( Model model, WebRequest req, WebResponse res, Cookie cookie, Locale locale, HttpServletRequest sreq, HttpServletResponse sres) { ・・・ 65
    • その他の機能•  Session管理 ‒  @SessionAttributes(model名)をクラスに設定すると、Modelに追加 したオブジェクトはHttpSessionに追加される ‒  @ModelAttribute(model名)を引数に設定すると、Sessionのオブジェ クトが引数に渡される ‒  SessionStatus#setComplete()でHttpSession内のオブジェクトが破 棄される •  ControllerごとにSession内のオブジェクトを管理可能•  入力チェック(Validation) ‒  JSR-303(Bean Validation)に対応 •  @NotNull String id; •  @Length(max = 30) String name; ‒  Validation対象の引数に@Validを設定する•  例外処理 ‒  例外発生時に実行するメソッドに@ExceptionHandler (FooException.class) 66
    • BON VOYAGE! 67