Weitere ähnliche Inhalte Ähnlich wie Spring3.1概要 AOP & MVC (20) Mehr von Yuichi Hasegawa (15) Kürzlich hochgeladen (11) Spring3.1概要 AOP & MVC2. 問題
• インスタンス変数に宣言したのと同じ型
のインスタンスをインジェクションして
もらうためのアノテーションは?
• インスタンス化され、インジェクション
されるために、クラス宣言の前に書くア
ノテーションは?
• 結局、DIコンテナって何のためにあるの
か?
2
5. 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
11. After Returning Advice
• Joinpointが正常終了した後に実行される
method()
return
Client After Returning Advice Servant
Exception
メソッドの実行後に正常終了時
に割り込む
11
16. AOPの主な利用方法
• 各クラスに記述されている同一の処理を抜き出し、ひと
まとめにして、既存のクラスに後から追加する
‒ ライブラリとの違い
• ライブラリは呼び出さないといけない
• AOPは勝手に追加される
• 追加すると便利な処理
‒ トランザクション管理
• トランザクション管理は難しいくプログラマに任せられない
‒ ログ管理
• メソッドの開始と終了のトレースログが正しく出力されない
‒ 誰もフォーマットを守らない
‒ トレースログを追加し忘れる
‒ 例外管理
• 処理の途中でExceptionが握りつぶされてしまう
‒ Exceptionを実行時例外にする
16
18. SpringのAOP
• 定義ファイルの利用
‒ Spring1.x系では基本
• アノテーションの利用
‒ Spring2.x系以降、アノテーションの利用が増
えている(大規模開発や大手SI便だでは定義
ファイルの利用が多い)
18
19. 問題 書いてないけど?
• 例えば
‒ アノテーション
• メリット:定義ファイルの管理が不要
• デメリット:プログラマにアノテーションを意識
‒ とか、定義ファイルのメリットとかデメリット
19
22. アノテーション一覧
• アノテーションを利用したAOP
‒ Bean定義ファイルの記述が簡潔になる
‒ ソースコードに記述することで管理が煩雑
アノテーション
説明
@Aspect Adviceとなるクラスを指定するアノテーション
@Around Around Adviceとなるメソッドを指定するアノテーション
@Before Before Adviceとなるメソッドを指定するアノテーション
@After After Adviceとなるメソッドを指定するアノテーション
@AfterReturning
After Returning Adviceとなるメソッドを指定するアノテーション
@AfterThrowing After Throwing Adviceとなるメソッドを指定するアノテーション
22
23. アドバイス詳細(1)
• Before, After
‒ @After( Primitiveポイントカット )
• メソッド名は任意、メソッドのパラメータと戻り値はなしでも可能。
メソッド内で、アスペクト対象となっているメソッド名やパラメー
タ,戻り値などの取得をする場合は、パラメータにJoinPoint
• メソッド内で、アスペクト対象となっているメソッドを呼び出す必
要はない
@Before(“execution(* exMethod())”)
public void hoge() {
・・・
}
23
24. アドバイス詳細(2)
• Around
‒ @Around( Primitiveポイントカット )
• メソッド名は任意、メソッドのパラメータには必
ずProceedingJoinPointが必要、戻り値はアスペ
クト対象のメソッドにあわせる
• メソッド内で、アスペクト対象となっているメ
ソッドを呼び出す必要がある
‒ ProceedingJoinPoint#proceed()メソッド
» Object proceed() throws Trowable
• メソッド内で、アスペクト対象となっているメ
ソッド名やメソッドのパラメータの取得は
ProceedingJoinPointを介しておこなう
24
25. アドバイス詳細(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
26. アドバイス詳細(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
27. アドバイス詳細(5)
• Around
‒ ProceedingJoinPointの使い方
メソッド名の取得
Signature sig = pjp.getSignature();
System.out.println("Sig: " + sig.getName());
パラメータの取得(最初のパラメータ)
Object[] os = pjp.getArgs();
System.out.println("Args: " + os[0]);
27
28. 問題∼重複してたらどうなる?
@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
29. アドバイス詳細(6)
• AfterReturning
‒ @AfterReturning(value= Primitiveポイント
カット , returnig = 戻り値の変数名 )
• メソッド名は任意、メソッドのパラメータはアス
ペクト対象となっているメソッドの戻り型とアノ
テーションのretuning属性で指定した変数名
• メソッド内で、アスペクト対象となっているメ
ソッドを呼び出す必要はない
29
30. アドバイス詳細(7)
public String exMethod() {
return “hello!”; アスペクト対象のメソッド
}
@AfterReturning(value=“execution(* exMethod())”,
returning=“ret”)
public String hoge(String ret) {
System.out.println(“Return: “ + ret);
}
30
31. アドバイス詳細(8)
• AfterThrowing
‒ @AfterReturning(value= Primitiveポイント
カット , throwing = 例外の変数名 )
• メソッド名は任意、メソッドのパラメータはアス
ペクト対象となっているメソッドの戻り型とアノ
テーションのthrowing属性で指定した変数名
• メソッド内で、アスペクト対象となっているメ
ソッドを呼び出す必要がない
31
33. アドバイス詳細(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
35. Primitiveポイントカット
Primitive
ポイントカット
概要
execution
呼出先の「メソッド」、「コンストラクタ」を指定する。
within
呼出元の「クラス」を指定する。
withinをPointcutに指定すると、指定されたクラスから呼出される、メソッ
ド等が選択されることになる。
対象は、指定されたクラスで宣言されたメソッドに限定され、指定された
クラスの親クラスで宣言されたメソッド内は対象外となる。
this
呼出元の「クラス」を指定する。
thisをPointcutに指定すると、指定されたクラスから呼出される、メソッド
等が選択されることになる。
withinとは、親クラスで定義されたメソッドの呼出しも対象とする点が異な
る。
target
呼出先の「クラス」を指定する。
ただし、呼出先のstaticフィールドは対象外となる。
args
呼出先「メソッド」の引数の型を指定する。
※Primitiveポイントカット:あらかじめ用意されているポイントカットのこと
35
36. executionの基本構文
• execution(メソッドの修飾子△メソッドの戻り値型△
パッケージ.クラスまたはインタフェース.メソッド名
(仮引数の型|,仮引数の型…|) △throws 例外)
• 「メソッドの修飾子(publicやprivateは省略可能)」や
「throws△例外」は省略することが可能
• メソッドの戻り値型、パッケージやクラス名、インタ
フェース名にはワイルドカード(*)の利用が可能
• 仮引数に(..)を記述すると任意の個数の引数と一致さ
せることが可能
36
37. ポイントカットで利用できる論理演算子
論理 説明
演算子
|| または 論理和を意味する論理演算子
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
38. コーディング例(1)
@Aspect
public 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
40. コーディング例(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
41. 定義ファイル
<?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
43. それでも書きたい定義ファイル(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
44. それでも書きたい定義ファイル(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
45. コーディング例
public class AspectMessage {
public void after() {
// メソッド終了後に動作するAdvice
System.out.println("after called");
}
public void before() {
// メソッド開始時に動作するAdvice
System.out.println("before called");
}
・・・以下省略
45
46. 問題 消すことできる?
<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
48. アーキテクチャ・リファクタリング(1/5)
Employee
Employee
FindAction MySql RDB
Service
Dao
ブラウザ
• 表示と永続化のフレームワーク導入済
• インタフェース未使用(もちろんDI,AOPも)
‒ チーム開発がしずらい
‒ 変更、機能拡張、テストが容易ではない
• 連続性も阻害
‒ Conecctionの引き回し、検査時例外の伝搬
48
49. アーキテクチャ・リファクタリング(2/5)
インタフェースの導入
Employee Employee
Service Dao
<<Singleton>> <<Singleton>>
FindAction Employee Employee MySQL
ServiceImpl DaoMySql
ブラウザ
RDB
利用
利用
生成
生成
Factory
• メリット
‒ インタフェースを区切りとして、チーム開発がやりやすくなった
‒ 変更、機能拡張、テストが容易になった
• デメリット
‒ Factoryを実装する必要がある
‒ クラスはFactoryに依存する
49
50. アーキテクチャ・リファクタリング(3/5)
DIの導入
Employee Employee
Service Dao
Employee Employee
FindAction MySQL
ServiceImpl DaoMySql
ブラウザ
RDB
生成
生成
Injection Injection
DIコンテナ
• Factoryを実装する必要がない
• クラスはDIコンテナに依存しない
50
51. アーキテクチャ・リファクタリング(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
52. アーキテクチャ・リファクタリング(5/5)
AOP導入後のソースコード
• 共通ライブラリを廃止してAOPを導入
• 連続性の確保
‒ トランザクション管理、ログ出力、例外処理はAOPで実現しているため、ソースコード上から
はなくなっている
‒ Advice実装されており、なくなっている訳ではない
• ソースコードの記述量が減り、バグの数も低下、開発者の作業も軽減
• テストの容易性も向上
public class EmployeeServiceImpl ! 書くことがなくなりました・・・!
implements EmploeeService {!
@Autowired!
private EmployeeDao dao;!
public List findAll() {!
return dao.findAll();!
}!
・・・
52
53. アーキテクチャ・リファクタリングの嘘
• AOPで業務例外(例えば、在庫がなかった時に
どうする)は処理できないから、そんなに奇麗
に例外は消えない(多分…)
• だって、AOPを使う基盤チームは業務を知らな
い(多分…)。だから、業務例外はAOPで提供
できない
• それに業務例外がAOPになったら、業務プログ
ラムが読めない!
• そもそも、業務例外にExceptionを使うのって
どうよ(!?)という問題でもある
53
55. Spring MVCとは
• Spring Frameworkに含まれるWeb
MVCフレームワーク
‒ 初期のSpring Frameworkの段階から含まれ
ている
‒ StrutsやJSFと競合
• 特徴
‒ DIコンテナとの親和性
‒ きれいな設計
• インタフェースを使用して部品化
55
56. Spring MVCのController
DIの導入
Employee Employee
Service Dao
Employee Employee
XxxController MySQL
ServiceImpl DaoMySql
ブラウザ
RDB
生成
生成
生成
Injection
Injection
DIコンテナ
56
58. 簡単になったSpring MVC
• Spring 3.xのSpring MVC
‒ Springの新機能を導入
• アノテーションにより設定がシンプルになり、わ
かりやすくなった
• component-scanにより設定ファイルが最低限で
すむようになった
‒ あいかわらず日本語の情報は少ない
• 英語のマニュアルを読みましょう
58
59. デモ
• STS(SpringSource Tool Suite)で作成
1. メニューからNew->Project
2. SpringSource Tool Suite->Spring
Template Projectを選択
3. Spring MVC Projectを選択
59
60. 動作概要
<<controller>>
Model
HomeController
"serverTime"
Date
home()
③
(現在の日時)
Dispatcher
ブラウザ
①
Servlet
(ほか色々)
<<jsp>>
/WEB-INF/views/home.jsp
⑥
60
61. 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
62. 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
64. 引数色々①
// リクエストパラメータを取得(「/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
65. 引数色々②
// 画面からの入力をマッピング(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
66. その他の機能
• 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