独自アスペクトの作り方
独自アスペクトの作り方
hifiveのアスペクトは、target, interceptors, pointCutの3つのプロパティを持つオブジェクトです。
基本的な形は以下のようになります。
target: {String | RegExp},
interceptors: {Function | Function[]},
pointCut: {String | RegExp}
};
ターゲットとポイントカット
ターゲットはインターセプタをどのコントローラ、ロジックに適用するかを、ポイントカットはどのメソッドに適用するかを定義します。
- ターゲットは、コントローラ、ロジックを定義する時の必須プロパティ__nameに対して
指定された値で判定を行います。 - ポイントカットは、ターゲットにマッチしたコントローラ、ロジックのメソッド名に対して
指定された値で判定を行います。 - インターセプタが適用されるのは、ターゲット、ポイントカットの両方にマッチした時のみです。
- ターゲットとポイントカットは省略することができます。省略した場合、それぞれ「すべてのコントローラ、ロジック」、「すべてのメソッド」という意味になります。
ターゲット、ポイントカットには正規表現と文字列を指定することができます。
正規表現の場合
指定された正規表現でそのまま判定を行います。
文字列の場合
- *による部分一致のみが可能になります。
- .や^など正規表現において特別な意味を持つものについては*を除いてすべてエスケープします。
- 暗黙的に始まりと終わりを追加します(/^{指定した文字列}$/ となります)。
- 例としてターゲットをjp.co.nssol.sample.controller*としたとします。
- __nameプロパティが"jp.co.nssol.sample.controller.TestController"であるコントローラはマッチします。
- __nameプロパティが"jp.co.nssol.sample2.controller.TestController"であるコントローラはマッチしません。
インターセプタ
インターセプタは実行したい処理を定義します。
var interceptor = function(invocation) {};
インターセプタは引数invocationを取る関数として定義します。
インターセプタ内のthisはコントローラで適用された場合そのコントローラ自身を、ロジックで適用された場合そのロジック自身となります。
invocationのメソッドプロパティは以下の通りです。
args
- 元のメソッドに渡された引数が格納されています。型はArgumentsです。
funcName
- 元のメソッド名が格納されています。
proceed
- 次のインターセプタ、もしくは元のメソッドを実行します。
h5.u.createInterceptor
インターセプタは定型の実装になることが多いため、ユーティリティを用意しています。
- preはインターセプト先関数の実行前に呼ばれる関数です。
- postはインターセプト先関数の実行後に呼ばれる関数です。
- 詳細についてはこちらを参照してください。
以下は関数の実行前後にアラートを表示するインターセプタの例です。
alert('before');
return invocation.proceed();
}, function(invocation, data) {
alert('after');
});
適用方法
アスペクトはh5preinitイベントでh5.settings.aspectsに指定します。
var aspect = {
interceptors: function() { console.log('aspect execute.'); }
};
h5.settings.aspects = aspect;
});
インターセプタの実行順序
- アスペクト、またアスペクトのinterceptorsプロパティは値として配列を取ることができます。
- 例えば以下のようにアスペクトを指定した場合、
var aspect1 = {
interceptors: [func1, func2]
};
var aspect2 = {
interceptors: func3
};
h5.settings.aspects = [aspect1, aspect2];
});
あるメソッドにかかるインターセプタの実行順は、
func1 -> func2 -> func3 -> 本来のメソッド
となります。
インターセプタの実行順は定義した順と一致します。
- もちろん、ターゲット、ポイントカットにマッチしない場合は、インターセプタは実行されません。
実装例
ここではボタンを押すと、ループを100万回回すロジックのメソッドの開始と終了をコンソールに出力するサンプルアプリケーションを作成します。
- HTML
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="-1">
<script src="jquery.js"></script>
<script src="ejs-1.0.h5mod.js"></script>
<script src="jquery.blockUI.js"></script>
<!-- hifiveのpreinitイベントで処理をさせるために、h5.jsより先に読み込む -->
<script src="h5preinit.js"></script>
<!-- 各モジュールのソースJSを読み込む -->
<script src="../../archives/current/h5.js"></script>
<!-- ここで作成したjsファイルを読み込む -->
<script src="step13-2.js"></script>
<title>hifive Aspectの適用</title>
</head>
<body>
<div class="browserNotification">
※ブラウザはできるだけ最新のバージョンをご利用ください。<br>
特に、IE6/7等の古いブラウザでは正しく動作しない場合があります。
</div>
<div id="container">
<input type="button" id="btn" value="click"/>
</div>
</body>
</html>
HTMLはボタンがあるだけです。
- アスペクト
var aspect = {
target: /^loop.*$/i,
interceptors: function(invocation) {
this.log.info(invocation.funcName + 'が開始されました。');
invocation.proceed();
this.log.info(invocation.funcName + 'が終了しました。');
},
pointCut: 'lo*'
};
h5.settings.aspects = aspect;
});
invocationの実行(invocation.proceed())前後にコンソールにメッセージを出力しています。
ターゲットは正規表現で指定します。意味は「大文字小文字関係なく"loop"で始まるもの」です。
ポインカットは文字列です。*を使って部分一致を行っています。意味は「loで始まるもの」です。
- ロジック
__name: 'LoopLogic',
loop: function() {
for (var i = 0; i < 1000000; i++) {
if (i % 10000 === 0) h5.log.info(i);
}
}
};
loopメソッドはループを100万回回し、10000回ごとに周回数をコンソールに出力しています。
- コントローラ
__name: 'LoopController',
loopLogic: loopLogic,
'#btn click': function() {
this.loopLogic.loop();
}
};
h5.core.controller('#container', loopController);
ボタンがクリックされると、LoopLogic#loopを呼んでいます。