AspectJ ログ

AspectJの概要

  • Javaのメソッドに対して、横断的に処理を追加する技術
    • たとえば、すべての public メソッドの開始・終了時にログを出力する処理を追加するなど
  • 用語
    • Aspect = Java の Class に相当。Advice の集合体
    • Advice = Java の Method に相当。Method の開始・終了時に埋め込む処理。
    • Point Cut = Advice を埋め込むことができるところ。Method の開始・終了など。
    • Join Point = Point Cut のうち、実際に Advice が埋め込まれたところ。Advice 側からは、JoinPoint?オブジェクト を通して Method や Object にアクセスできる。
  • 当初 Xerox パロアルト研究所で作られた概念。現在 Eclipse.org に移管されて開発中。
  • まずは、Aspect 指向プログラミングの代表例*1ともいうべきログ出力をやってみる

Eclipse への ADJT プラグインのインストール

AspectJプロジェクトの作成

[ファイル]-[新規]-[その他]

Ajdt3.png

こんなアプリを作る

  • おみくじアプリ
    Ajdt4.png
    ボタンを押すと、OmikujiModel?.createOracle() を呼び出して、おみくじを引く。6回おみくじを引くとエラーになる
  • OmikujiUI (ボタン押下イベント部分のみ)
    jButton.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent e) {
        try{
          System.out.println("UI wrote : START actionPerformed()");
          
          System.out.println("UI wrote : CALL  OmikujiModel.createOracle()");
          String oracle = OmikujiModel.createOracle();
          jLabel.setText(oracle);
          System.out.println("UI wrote : RET   OmikujiModel.createOracle()");
          
          System.out.println("UI wrote : TERM  actionPerformed()");
          return;
        }catch(Exception ex){
          jLabel.setText("ERROR");
        }finally{
          System.out.println("UI wrote : FINAL actionPerformed()");
        }
      }
    });
  • OmikujiModel?
    Everything is expanded. Everything is shortened.
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
    
     
     
    -
    |
    |
    |
    |
    -
    -
    |
    |
    |
    -
    |
    !
    !
    !
    
    package com.snail.exam.aj.omikuji;
     
    public class OmikujiModel {
      private static int count = 0;
      private static final String[] ORACLES 
      = new String[]{"excellent lucky","lucky","a bit lucky","misfortune","great misfortune"};
      
      public static String createOracle(){
        try{
          System.out.println("MODEL wrote : START createOracle()");
          System.out.println("MODEL wrote : TERM  createOracle()");
          return ORACLES[ (count++) ];
        }finally{
          System.out.println("MODEL wrote : FINAL createOracle()");
        }
      }
    }

ログ出力のAspectを適用する

Everything is expanded. Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 
 
 
 
 
 
 
 
 
 
 
 
-
|
|
|
|
|
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
|
-
|
!
|
-
|
|
|
|
!
|
!
package com.snail.exam.aj.omikuji;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
 
@Aspect
public class OmikujiLogger {
  @Pointcut("execution(public * com.snail.exam.aj.omikuji.*.*(..)) && !within(OmikujiLogger)")
  public void someMethodExecute() {}
  
  @Pointcut("call(public * com.snail.exam.aj.omikuji.*.*(..)) && !within(OmikujiLogger)")
  public void someMethodCall() {}
  
  @Before("someMethodExecute()")
  public void beforeExecute(JoinPoint thisJoinPoint){
    dumpJoinPoint("beforeExecute()",thisJoinPoint);
  }
 
  @After("someMethodExecute()")
  public void afterExecute(JoinPoint thisJoinPoint){
    dumpJoinPoint("afterExecute()",thisJoinPoint);
  }
  
  @AfterReturning("someMethodExecute()")
  public void returnExecute(JoinPoint thisJoinPoint){
    dumpJoinPoint("returnExecute()",thisJoinPoint);
  }
  
  @AfterThrowing("someMethodExecute()")
  public void throwExecute(JoinPoint thisJoinPoint){
    dumpJoinPoint("throwExecute()",thisJoinPoint);
  }
 
  @Before("someMethodCall()")
  public void beforeCall(JoinPoint thisJoinPoint){
    dumpJoinPoint("beforeCall()",thisJoinPoint);
  }
 
  @After("someMethodCall()")
  public void afterCall(JoinPoint thisJoinPoint){
    dumpJoinPoint("afterCall()",thisJoinPoint);
  }
 
  @AfterReturning("someMethodCall()")
  public void returnCall(JoinPoint thisJoinPoint){
    dumpJoinPoint("returnCall()",thisJoinPoint);
  }
  
  @AfterThrowing("someMethodExecute()")
  public void throwCall(JoinPoint thisJoinPoint){
    dumpJoinPoint("throwCall()",thisJoinPoint);
  }
 
  private void dumpJoinPoint(String src,JoinPoint joinPoint){
    Signature sig = joinPoint.getSignature();
    String jPointName = sig.toLongString();
    
    System.out.println(src+"\t("+jPointName+")");
  }
 
}
  • Point Cut の指定の仕方
    @Pointcut("execution(public * com.snail.exam.aj.omikuji.*.*(..)) && !within(OmikujiLogger)")
    • パラーメータの意味
      execution実行時
      publicpublic属性のメソッド
      *返値は何でも良い
      com.snail.exam.aj.omikuji.パッケージの
      *.どのクラスでも良い
      *どのメソッドでも良い
      (..)引数は任意
    • !(within(OmikujiLogger?))
      • OmikujiLogger?(自分)は除く
      • これがないと、ログ出力アドバイス(自分)にアドバイスが適用されて無限ループになる
  • call と execution
    • call は、メソッド呼び出し時。Join Point は、呼び出し側
    • execution は、メソッド実行時。Join Point は、呼び出し先
    • @Around アドバイスを使うときには、使い分けが必要 → AspectJ GUIアプリの国際化
    • @Before / @After アドバイスでは余り関係ないかも
  • アドバイスについて
    @Before
    @After
    @AfterReturning?リターン後
    @AfterThrowing?例外発生後
    @AroundMethod をまるまる Advice に置き換えるときに使う

アドバイスの発生順序

ボタンを押したときに、Method と Advice がどういう順番で動くのか? 実際にこの「おみくじプログラム」を動かしてみた。

  • 正常時
    UI wrote : START actionPerformed()
    UI wrote : CALL  OmikujiModel.createOracle()
    beforeCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    beforeExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    MODEL wrote : START createOracle()
    MODEL wrote : TERM  createOracle()
    MODEL wrote : FINAL createOracle()
    afterExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    returnExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    afterCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    returnCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    UI wrote : RET   OmikujiModel.createOracle()
    UI wrote : TERM  actionPerformed()
    UI wrote : FINAL actionPerformed()
  • 例外発生時
    UI wrote : START actionPerformed()
    UI wrote : CALL  OmikujiModel.createOracle()
    beforeCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    beforeExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    MODEL wrote : START createOracle()
    MODEL wrote : TERM  createOracle()
    MODEL wrote : FINAL createOracle()
    afterExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    throwExecute()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    throwCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    afterCall()	(public static java.lang.String com.snail.exam.aj.omikuji.OmikujiModel.createOracle())
    UI wrote : FINAL actionPerformed()

ログの埋め込みだけでもありがたい

  • 想像してみてください。
  • もしもあなたが、全くログを出さず、仕様書もなく、担当者もどこかへ消えたスパゲッティー・プログラムの改修を任されたとしたら・・・
  • そんなとき、AspectJ を使って、すべてのメソッドの開始時と終了時にログを埋め込んでやれば、問題箇所がどのクラスのどのメソッドなのかを特定できる。地獄に仏 とは正にこのこと
  • かぐや・・・

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值