『Spring攻略』第3章Spring AOPとAspectJサポート


1 SpringのAspectJ注記サポートを有効にする
質問:Springは、AOPフレームワークでAspectJ注釈を使用して作成されたPOJO aspectをサポートします.ただし、SpringのAspectJ注釈サポートを最初に有効にする必要があります.ソリューション:Beanプロファイルに空のXML要素を定義するだけで、Spring IoCコンテナのAspectJ注釈サポートを有効にできます.その後、SpringはあなたのAspectJ aspectに一致するすべてのBeanのエージェントを自動的に作成します.インタフェースが使用できない、またはアプリケーション設計に使用されていない場合は、CGLIBに依存してエージェントを作成できます.CGLIBを有効にするには、でproxy-targetclass=true属性を設定する必要があります.
2 AspectJ注記でaspectを宣言する
  :
  5  AspectWerkz   ,AspectJ    aspect       AspectJ   POJO。
Spring AOP       aspect,    aspect   Spring IoC         。

    :
  Spring   AspectJ aspect,         IoC    Bean     。
 Spring IoC     AspectJ,        AspectJ aspect Bean    。

 AspectJ     aspect      @Aspect   java 。  (Advice)            Java  。
AspectJ  5     :@Before、@After、@AfterReturning、@AfterThrowing @Around。

    :
    :
@Before

   :    ,      
@Aspect
public class LoggingBeforeAspect {
	private Log log = LogFactory.getLog(LoggingBeforeAspect.class);
	
	@Before("execution(* *..*.delete(..))")
	public void logBefore(){
		log.debug("The method add() begins");
	}
}
     class   IoC   ,          
<aop:aspectj-autoproxy />
<bean id="userDao" class="com.partner4java.aspectj.demo1.UserDaoImpl"/>
<bean class="com.partner4java.aspectj.demo1.LoggingBeforeAspect"/>

   :          
@Aspect
public class LoggingBeforeAspect1 {
	private Log log = LogFactory.getLog(LoggingBeforeAspect1.class);
	
	@Before("execution(* *..*.delete(..))")
	public void logBefore(JoinPoint joinPoint){
		log.debug("The method "+ joinPoint.getSignature().getName() +" add() begins");
	}
}


    :
    (after advice)          ,            。
@Aspect
public class LoggingAfterAspect {
	private Log log = LogFactory.getLog(LoggingAfterAspect.class);

	@After("execution(* *..*.delete(..))")
	public void logBefore(JoinPoint joinPoint) {
		log.debug("The method " + joinPoint.getSignature().getName() + " ends");
	}
}


    :
                      。
               ,       (after returning advice)      。
      ,    @AfterReturning       returning  ,         。                 ,       。
@Aspect
public class LoggingAfterReturnAspect {
	private Log log = LogFactory.getLog(LoggingAfterReturnAspect.class);

	@AfterReturning(pointcut = "execution(* *..*.delete(..))", returning = "result")
	public void logBefore(JoinPoint joinPoint, Object result) {
		log.debug("The method " + joinPoint.getSignature().getName()
				+ " ends with" + result);
	}
}


    :
            。
@Aspect
public class LoggingErrorAspect {
	private Log log = LogFactory.getLog(LoggingErrorAspect.class);

	@AfterThrowing(pointcut = "execution(* *..*.save(..))", throwing = "throwable")
	public void logAfterThrowing(JoinPoint joinPoint, Throwable throwable) {
		log.debug("exception " + throwable + " in method"
				+ joinPoint.getSignature().getName());
	}
}


    :
      ,           ,           。
@Aspect
public class LoggingAroundAspect {
	private Log log = LogFactory.getLog(LoggingErrorAspect.class);
	
	@Around("execution(* *..*.find(..))")
	public Object logAround(ProceedingJoinPoint joinPoint){
		log.debug("being " + joinPoint.getSignature().getName());
		
		try {
			Object result = joinPoint.proceed();
			log.debug(" end " + result);
			return result;
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		return null;
	}
}

                            。

3接続ポイント情報へのアクセス
質問:AOPでは、通知は接続ポイントと呼ばれる異なるプログラム実行ポイントに適用されます.通知が正しい行動をとるためには,接続点の詳細が必要になることが多い.解決策:通知は、通知メソッド署名にJoinPointタイプのパラメータを宣言し、現在の接続ポイント情報にアクセスできます.
4 aspect優先度の指定
問題:同じ接続ポイントに複数のaspectが適用される場合、定義が表示されていない限り、メソッドの優先度は明確ではありません.ソリューション:aspectの優先度は、Orderedインタフェースを実装するか、@Order注釈を使用して完了できます.
5切り込みポイント定義の再利用
  :
   aspect ,                   。  ,                   。

    :
     AOP    ,AspectJ           ,        。

    :
           ,     private   ,       ,       ,           。

@Aspect
public class LoggingAspectj {
	@Pointcut("execution(* *..*.find(..))")
	private void loggingOpertion(){}
	
	@Before("loggingOpertion()")
	public void logBefore(){
		
	}
}

  ,     loggingOpertion   public ,            @Before("LoggingAspectj.loggingOpertion()"),
        。

6 AspectJ接点式の作成
問題:横断的な注目点は、接続点と呼ばれる異なるプログラム実行点で発生する可能性があります.接続点が多様化しているため、マッチングを支援する強力な表現言語が必要です.ソリューション:
7あなたのBeanに行動を導入
質問:公共行為を共有するクラスがある場合があります.OOPでは、同じベースクラスを拡張したり、同じインタフェースを実装したりする必要があります.この問題は確かにAOPモジュール化が可能な横断点の注目点である.さらに、Javaの単一継承メカニズムでは、クラスを最大1つのベースクラスに拡張できます.したがって、複数のインプリメンテーションクラスから動作を同時に継承することはできません.ソリューション:導入(Introduction)はAOPの特別な通知です.彼は1つのインタフェースに実装クラスを提供し、オブジェクトを動的に実装インタフェースを提供することを許可します.これは、実行時に実装クラスを拡張するように見えます.また、複数のインプリメンテーションクラスで複数のインタフェースを同時にオブジェクトに導入できます.これにより、多重継承と同じ効果が得られます.動作原理:@DeclareParents
8あなたのBeanに状態を導入
  :
   ,                   ,         ,     、       。
              ,      。
  ,                 ,          。


    :
                           。  ,                     。


    :
             ,    AspectJ       。

<aop:aspectj-autoproxy />

<bean id="userDao" class="com.partner4java.aspectj.demo1.UserDaoImpl"/>

<bean class="com.partner4java.aspectj.declare.DeclareLogAspectj"/>

@Aspect
public class DeclareLogAspectj {

	//value          
	//     ,        ,   defaultImpl    
	@DeclareParents(value = "com.partner4java.aspectj.demo1.*", defaultImpl = ExecuteLogImpl.class)
	public ExecuteLog executeLog;

	//
	@Around("execution(* *.*(..))" + " && this(executeLog)")
	public Object declareLog(ProceedingJoinPoint joinPoint,
			ExecuteLog executeLog) {
		executeLog.setBeginDate(new Date());

		try {
			Object result = joinPoint.proceed();
			executeLog.increase();
			executeLog.setEndDate(new Date());
			return result;
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}
}

                          :
@Test
public void testAsp(){
	UserDao userDao = (UserDao) applicationContext.getBean("userDao");
	userDao.find(new Integer(22));
	
	ExecuteLog executeLog = (ExecuteLog)userDao;
	System.out.println(executeLog.getCount());
	System.out.println(executeLog.getBeginDate());
}

9 XMLベースの構成でaspectを宣言
  :
                 。

    :
  Spring XML    。

    :
    <aop:aspectj-autoproxy />       。

<aop:config>
    <aop:pointcut id="loggingOperation" expression=
        "within(com.apress.springrecipes.calculator.ArithmeticCalculator+) || within(com.apress.springrecipes.calculator.UnitCalculator+)" />

    <aop:pointcut id="validationOperation" expression=
        "within(com.apress.springrecipes.calculator.ArithmeticCalculator+) || within(com.apress.springrecipes.calculator.UnitCalculator+)" />

    <aop:aspect id="loggingAspect" ref="calculatorLoggingAspect">
        <aop:before pointcut-ref="loggingOperation"
            method="logBefore" />

        <aop:after-returning pointcut-ref="loggingOperation"
            returning="result" method="logAfterReturning" />

        <aop:after-throwing pointcut-ref="loggingOperation"
            throwing="e" method="logAfterThrowing" />

        <aop:around pointcut-ref="loggingOperation"
            method="logAround" />
    </aop:aspect>

    <aop:aspect id="validationAspect" ref="calculatorValidationAspect">
        <aop:before pointcut-ref="validationOperation"
            method="validateBefore" />
    </aop:aspect>

    <aop:aspect id="introduction" ref="calculatorIntroduction">
        <aop:declare-parents
            types-matching=
                "com.apress.springrecipes.calculator.ArithmeticCalculatorImpl"
            implement-interface=
                "com.apress.springrecipes.calculator.MaxCalculator"
            default-impl=
                "com.apress.springrecipes.calculator.MaxCalculatorImpl" />

        <aop:declare-parents
            types-matching=
                "com.apress.springrecipes.calculator.ArithmeticCalculatorImpl"
            implement-interface=
                "com.apress.springrecipes.calculator.MinCalculator"
            default-impl=
                "com.apress.springrecipes.calculator.MinCalculatorImpl" />

        <aop:declare-parents
            types-matching=
                "com.apress.springrecipes.calculator.*CalculatorImpl"
            implement-interface=
                "com.apress.springrecipes.calculator.Counter"
            default-impl=
                "com.apress.springrecipes.calculator.CounterImpl" />

        <aop:after pointcut=
            "execution(* com.apress.springrecipes.calculator.*Calculator.*(..)) and this(counter)"
            method="increaseCount" />
    </aop:aspect>
</aop:config>




10 SpringでのAspectJロード時にaspectを織り込む
質問:Springでは、制限されたAspectJ接点タイプのみがサポートされており、IoCコンテナでBeanを宣言するためにaspectを適用できます.より多くのカットポイントタイプを使用するか、Spring IoCコンテナ以外で作成したオブジェクトにaspectを適用する場合は、SpringアプリケーションでAspectJフレームワークを使用する必要があります.ソリューション:織り込み(Weaving)は、ターゲットオブジェクトにaspectを適用するプロセスです.Spring AOPを使用すると、注入実行時に動的エージェントによって発生します.逆に、AspectJフレームワークでは、コンパイル時とロード時の織り込みがサポートされています.AspectJコンパイル時の織り込みは特殊なAspectJコンパイラajcで完了します.このコンパイラはaspectをJavaリソースファイルに織り込み、織り込まれたバイナリクラスファイルを出力します.彼はまたaspectをあなたがコンパイルしたクラスファイルやJARファイルに織り込むこともできます.このプロセスをコンパイル後織り込みと呼ぶ.Spring IoCコンテナでクラスを宣言する前に、コンパイル時または後コンパイル時に織り込むことができます.Springは織り込みプロセスに全く関与しない.AspectJロード時の織り込み(LTWとも呼ばれる)は、ターゲットクラスがクラスローダからJVMにロードされたときに発生します.織り込みを行うクラスでは、ターゲットクラスのバイトコードを改善するために特殊なクラスローダが必要です.AspectJとSpringはいずれもロード時の織り込みプログラムを提供し,クラスローダにロード織り込み能力を増加させる.動作原理:..