Chapter 6. Spring을 이용한 Aspect 지향 프로그래밍

6.1. 소개

Aspect-지향 프로그래밍(AOP)는 프로그램 구조에 대한 다른 방식의 생각을 제공함으로써 객체지향 프로그래밍(OOP)를 보완한다. 클래스들에 추가적으로, AOP는 aspects를 제공한다. aspects는 다중 타입과 객체에 영향을 주는 트랜잭션 관리처럼 관심사의 모듈화를 가능하게 한다(이러한 관심사는 종종 crosscutting 관심사라는 용어로 사용된다.)

Spring의 핵심이 되는 컴포넌트중 하나는 AOP 프레임워크이다. Spring IoC컨테이너가 AOP에 의존하지 않는 동안, 당신이 원하지 않는다면 AOP를 사용할 필요가 없다는 것을 의미한다. AOP는 미들웨어 솔루션의 기능을 제공하기 위해 Spring IoC를 보완할것이다.

AOP는 Spring내에서 사용된다.

  • 선언적인 기업용 서비스를 제공하기 위해 EJB 선언적 서비스를 위한 대체물처럼 사용될수 있다. 서비스처럼 가장 중요한 것은 Spring의 트랜잭션 추상화에서 빌드되는 선언적인 트랜잭션 관리이다.

  • 사용자 정의 aspect를 구현하는 것을 사용자에게 허용하기 위해 AOP를 사용하여 OOP의 사용을 기능적으로 보완한다.

게다가 당신은 EJB없이 선언적인 트랜잭션 관리를 제공하는 것을 Spring에 허용하도록 하는 기술을 가능하게 하는것처럼 Spring AOP를 볼수 있다. 또는 사용자 지정 aspect를 구현하기 위한 Spring AOP프레임워크의 강력한 힘을 사용할수 있다.

이 장의 첫번째 AOP 개념의 소개는 당신이 사용하기 위해 선택하는 aspect선언의 스타일이 무엇이든 읽기를 원할것이다. 이 장의 나머지는 Spring 2.0 AOP지원에 중점을 둘것이다. Spring 1.2 스타일의 AOP의 개요를 위해 다음장을 보라.

만약 당신이 일반적인 선언적 서비스나 풀링과 같은 다른 미리 패키징된 선언적 미들웨어 서비스만 관심을 가진다면 당신은 Spring AOP를 사용하여 직접적으로 작업할 필요가 없다. 그리고 이 장의 대부분을 그냥 넘어갈수 있다.

6.1.1. AOP 개념

몇몇 중심적인 AOP개념을 명시함으로써 시작해보자. 이 개념들은 Spring에 종속적인 개념이 아니다. 운 나쁘게도 AOP전문용어는 특히 직관적이지 않다. 어쨌든 Spring이 그 자신의 전문용어를 사용했다면 좀더 혼란스러울것이다.

  • Aspect: 다중 객체에 영향을 주는 concern의 모듈화. 트랜잭션 관리는 J2EE애플리케이션의 crosscutting concern의 좋은 예제이다. Spring AOP에서, aspect는 정규 클래스나 @Aspect 어노테이션(@Aspect 스타일)으로 주석처리된 정규 클래스를 사용하여 구현된다.

  • Joinpoint: 메소드 수행이나 예외를 다루는 것과 같은 프로그램의 수행기간 동안의 point. Spring AOP에서, joinpoint는 언제나 메소드 호출을 나타낸다. Join point정보는 org.aspectj.lang.JoinPoint 타입의 파라미터를 선언하여 advice에서 사용가능하다.

  • Advice: 특정 joinpoint에서 aspect에 의해 획득되는 액션. advice의 다른 타입은 "around," "before" 과 "after" advice를 포함한다. advice 타입은 밑에서 언급된다. Spring을 포함한 많은 AOP프레임워크는 인터셉터로 advice를 모델화하고 joinpoint "주위(around)"로 인터셉터의 묶음(chain)을 유지한다.

  • Pointcut: join point에 일치하는 속성. advice는 pointcut표현과 관련있고 pointcut에 일치하는 join point에서 수행된다(예를 들어, 어떤 이름을 가진 메소드의 수행). pointcut표현에 의해 일치하는 join point의 개념은 AOP에 집중된다. Spring은 디폴트로 AspectJ pointcut언어를 사용한다.

  • Introduction: (중간(inter)-타입 선언으로 알려진) 타입에서 추가적인 메소드나 필드의 선언. Spring은 프록시화된 객체를 위해 새로운 인터페이스(와 관련 구현물)를 소개한다. 예를 들어, 간단한 캐시를 위해, IsModified 인터페이스를 구현하는 bean을 만들기 위한 소개(introduction)를 사용한다.

  • 대상 객체: 객체는 하나이상의 aspect에 의해 충고된다. 또한 advised 객체를 참조한다. Spring AOP가 런타임 프록시를 사용하여 구현되기 때문에, 이 객체는 언제나 프록시화된 객체가 될것이다.

  • AOP 프록시: aspect 규칙(advise메소드수행과 기타등등)을 구현하기 위하여 AOP프레임워크에 의해 생성되는 객체. Spring에서. AOP프록시는 JDK 동적 프록시나 CGLIB 프록시가 될것이다. 노트 : 프록시 생성은 스키마-기반과 Spring 2.0에서 소개된 aspect선언의 @AspectJ스타일의 사용자에게 명백하다.

  • Weaving: 다른 애플리케이션 타입이나 advised객체를 생성하기 위한 객체를 가지는 aspect 연결. 이것은 컴파일 시점(예를 들어, AspectJ 컴파일러를 사용하여), 로그시점 또는 런타임에 수행될수 있다. 다른 Java AOP프레임워크처럼 Spring은 런타임시 직조(weaving)를 수행한다.

advice 타입

  • Before advice: joinpoint전에 수행되는 advice. 하지만 joinpoint를 위한 수행 흐름 처리(execution flow proceeding)를 막기위한 능력(만약 예외를 던지지 않는다면)을 가지지는 않는다.

  • After returning advice: joinpoint이 일반적으로 예를 들어 메소드가 예외를 던지는것없이 반환된다면 완성된 후에 수행되는 advice.

  • After throwing advice: 메소드가 예외를 던져서 빠져나갈때 수행되는 advice

  • After (finally) advice: join point를 빠져나가는(정상적이거나 예외적인 반환) 방법에 상관없이 수행되는 advice.

  • Around advice: 메소드 호출과 같은 joinpoint주위(surround)의 advice. 이것은 가장 강력한 종류의 advice이다. Around advice는 메소드 호출 전후에 사용자 정의 행위를 수행할수 있다. 그것들은 joinpoint를 처리하거나 자기 자신의 반환값을 반환함으로써 짧게 수행하거나 예외를 던지는 것인지에 대해 책임을 진다.

Around advice는 가장 일반적인 종류의 advice이다. Nanning Aspects와 같은 대부분의 인터셉션-기반의 AOP프레임워크는 오직 around advice만을 제공한다.

AspectJ처럼 Spring이 advice타입의 모든 범위를 제공하기 때문에 우리는 요구되는 행위를 구현할수 있는 최소한의 강력한 advice타입을 사용하길 권한다. 예를 들어 당신이 메소드의 값을 반환하는 캐시만을 수정할 필요가 있다면 around advice가 같은것을 수행할수 있다고하더라도 around advice보다 advice를 반환한 후에 구현하는게 더 좋다. 대부분 특정 advice타입을 사용하는것은 잠재적으로 적은 에러를 가지는 간단한 프로그래밍 모델을 제공한다. 예를 들어 당신은 around advice를 위해 사용되는 JoinPointproceed()메소드를 호출할 필요가 없고 나아가 그것을 호출하는것을 실패할수도 있다.

Spring 2.0에서, 모든 advice파라미터는 정적으로 타입화된다. 그래서 객체 배열보다는 적절한 타입(예를 들면, 메소드 수행의 반환값의 타입)의 advice파라미터를 가지고 작업한다.

pointcut에 의해 일치하는 join point의 개념은 오직 인터셉션만을 제공하는 예전 기술과 구분되는 AOP의 핵심이다. pointcut은 OO구조의 단독으로 대상화되도록 해준다. 예를 들어, 선언적인 트랜잭션 관리를 제공하는 around advice는 다중 객체에 걸쳐있는 메소드에 적용될수 있다(서비스 레이어내 모든 비니지스 작동과 같은).

6.1.2. Spring AOP의 기능과 대상

Spring AOP는 순수자바로 구현되었다. 특별한 편집 절차가 필요하지 않다. Spring AOP는 클래스로더 구조를 제어할 필요가 없고 J2EE웹 컨테이너나 애플리케이션 서버내 사용되는것이 적합하다.

Spring은 현재 메소드 수행 join point만을 지원한다(Spring bean의 메소드 수행을 충고하는). 필드 인터셉션은 구현되지 않았지만 핵심 Spring AOP API에 영향없이 추가될수 있도록 필드 인터셉션을 지원한다. advice필드 접근과 join point수정이 필요하다면, AspectJ와 같은 다른 언어를 고려하라.

Spring AOP접근법은 대부분의 다른 AOP프레임워크와 다르다. 목적은 완벽한 AOP구현물을 제공하는 것이 아니다(비록 Spring AOP는 상당히 가능함에도). 전사적인 애플리케이션에서 공통의 문제를 푸는것을 돕기 위해 AOP구현물과 Spring IoC간에 닫힌(close) 통합을 제공한다.

게다가, 예를 들어, Spring의 AOP기능은 Spring IoC컨테이너와 결합되어 사용된다. Aspect는 일반적인 bean정의 문법을 사용하여 설정된다(강력한 "autoproxing" 기능을 허용하기는 하나.). 이것은 다른 AOP구현물과 결정적으로 다른점이다. 매우 잘 구성된 객체을 충고(advise)하는 것처럼 Spring AOP로 쉽거나 효과적으로 할수 없는 것이 있다. AspectJ는 이런 경우 가장 훌륭한 선택이다. 어쨌든, Spring AOP가 AOP를 다루기 쉬운 J2EE애플리케이션에서 공통의 문제를 위해 훌륭한 해결법을 제공한다고 경험했다.

Spring AOP는 포괄적인 AOP솔루션을 제공하기 위해 AspectJ와 경쟁하지 않을것이다. 우리는 Spring과 같은 프록시-기반의 프레임워크와 AspectJ와 같은 성숙한 프레임워크가 가치있고 경쟁보다는 상호보완한다고 믿는다. Spring 2.0은 일관적인 Spring 기반의 애플리케이션 아키텍처에서 AOP의 모든 사용이 가능하도록 AspectJ로 Spring AOP와 IoC를 균일하게 통합한다. 이 통합은 Spring AOP API나 AOP제휴 API에 영향을 주지 않는다. Spring AOP는 이전 버전과의 호환성을 유지한다. Spring AOP API에 대해서는 다음 장을 보라.

6.1.3. Spring 내 AOP 프록시

Spring은 AOP프록시를 위해 J2SE 동적 프록시(dynamic proxies)를 사용하는것이 디폴트이다. 이것은 프록시가 되기 위한 어떤 인터페이스나 인터페이스의 모음을 가능하게 한다.

Spring은 또한 CGLIB프록시도 사용가능하다. 이것은 인터페이스보다는 클래스를 프록시화 하기 위해 필요하다. CGLIB는 비지니스 객체가 인터페이스를 구현하지 않는다면 디폴트로 사용된다. 클래스보다는 인터페이스를 위한 프로그램을 위해 좋은 경험이기 때문에 비지니스 객체는 일반적으로 하나 이상의 비지니스 인터페이스를 구현할것이다.

이것은 강제로 CGLIB의 사용하도록 할수 있다. 우리는 이것을 밑에서 언급할것이다, 그리고 당신이 왜 이렇게 하는것을 원하는지 설명할것이다.

Spring 2.0이상에서, Spring은 아마도 전체적으로 생성되는 클래스를 포함해서 AOP프록시의 추가적인 타입을 제공할것이다. 이것은 프로그래밍 모델에는 영향을 끼치지 않을것이다.

6.2. @AspectJ 지원

"@AspectJ" 는 Java 5 어노테이션으로 주석처리된 정규 Java클래스로 aspect를 선언하는 스타일을 언급한다. @AspectJ스타일은 AspectJ 5 릴리즈의 일부처럼 AspectJ 프로젝트에 의해 소개되었다. Spring 2.0은 pointcut 파싱과 일치를 위해 AspectJ에 의해 제공되는 라이브러리를 사용하여 AspectJ 5처럼 같은 어노테이션을 해석한다. AOP런타임은 여전히 순수한 Spring AOP이고 AspectJ 컴파일러나 직조자(weaver)에 대한 의존성이 없다.

AspectJ 컴파일러와 직조자(weaver)를 사용하는 것은 AspectJ언어를 완전하게 사용가능하도록 해준다. 그리고 이것은 Section 6.8, “Using AspectJ with Spring applications”에서 언급된다.

6.2.1. @AspectJ 지원 가능하게 하기

Spring 설정에서 @AspectJ aspect를 사용하기 위해, 당신은 @AspectJ aspect와 aspect에 의해 충고되는지에 대한 유무에 기초한 autoproxying bean에 기초하도록 Spring AOP를 설정할 필요가 있다. autoproxying을 사용하여 우리는 bean이 하나 이상의 aspect에 의해 충고(advised)되는지 Spring이 판단하도록 한다. 이것은 메소드 호출을 가로채고 advice가 필요할때 수행되는지를 확인하는 bean을 위한 프록시를 자동으로 생성할것이다.

@AspectJ 지원은 Spring설정내부에 다음의 요소를 포함하는것으로 가능하게 된다.

<aop:aspectj-autoproxy/>

이것은 당신이 Appendix A, XML 스키마-기반 설정에서 언급된것처럼 스키마 지원을 사용한다는 것으로 추정한다. aop명명공간내 태그를 import하는 방법을 위해 Section A.2.6, “aop 스키마” 를 보라.

DTD를 사용한다면, 애플리케이션 컨텍스트에 다음의 정의를 추가하여 @AspectJ지원을 가능하게 할수 있다.

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

당신의 애플리케이션의 클래스패스에 두개의 AspectJ라이브러리(aspectjweaver.jaraspectjrt.jar)를 둘 필요가 있을것이다. 이러한 라이브러리는 AspectJ(1.5.1이나 그 이후버전이 필요)의 lib 디렉토리나 Spring-with-dependencies 배포판의 lib/aspectj 디렉토리에서 볼수 있다.

6.2.2. aspect 선언하기

@AspectJ 지원을 가능하게 하여, @AspectJ aspect(@Aspect 어노테이션을 가지는)인 클래스로 당신의 애플리케이션 컨텍스트내 정의된 bean은 Spring에 의해 자동적으로 감지되고 Spring AOP를 설정하기 위해 사용될것이다. 다음은 그다지 유용하지 않은 aspect를 위해 요구되는 최소한의 정의를 보여주는 예제이다.

애플리케이션 컨텍스트내 정규 bean정의는 @Aspect 어노테이션을 가지는 bean클래스를 가리킨다.

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
   <!-- configure properties of aspect here as normal -->
</bean>

그리고 NotVeryUsefulAspect 클래스 정의는 org.aspectj.lang.annotation.Aspect 어노테이션으로 주석처리된다.

package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

aspect(@Aspect로 주석처리된 클래스)는 다른 클래스처럼 메소드와 필드를 가진다. 그것들은 또한 pointcut, advice, 그리고 introduction(내부타입)선언을 가진다.

6.2.3. pointcut 선언하기

pointcut이 join point를 판단하고 advice가 수행될때 제어를 가능하게 해주는 것을 것을 상기해보자. Spring AOP는 Spring bean을 위한 메소드 수행 join point만을 지원한다., 그래서 당신은 pointcut을 Spring bean의 메소드 수행을 일치시키는 것으로 생각할수 있다. pointcut선언은 두개의 부분을 가진다. 시그너처는 이름(name)과 어떤 파라미터를 포함하고 부수적인 pointcut표현은 메소드 수행을 실제로 판단한다. @AspectJ 어노테이션 스타일의 AOP에서, pointcut시그너처는 정규 메소드 정의에 의해 제공되고 pointcut표현은 @Pointcut 어노테이션을 사용하여 표시된다(pointcut시그너처 처럼 제공하는 메소드는 void 반환타입을 가져야만 한다).

예제는 pointcut시그너처와 pointcut표현간의 구분을 가지도록 도와줄것이다. 다음 예제는 'transfer'라는 이름의 메소드의 수행과 일치할 'anyOldTransfer' 라는 이름의 pointcut을 정의한다.

@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

@Pointcut 어노테이션 값의 형태인 pointcut표현은 정규 AspectJ 5 pointcut표현이다. AspectJ의 pointcut언어의 완전한 언급을 위해서, AspectJ 프로그래밍 가이드를 보라(그리고 Java 5 기반의 확장을 위해서, AspectJ 5 Developers Notebook를 보라) 또는 Colyer가 쓴 "Eclipse AspectJ"나 Ramnivas Laddad가 쓴 "AspectJ in Action"중 하나를 보라.

6.2.3.1. 지원되는 Pointcut 지시자(Designators)

Spring AOP는 pointcut표현을 사용하기 위한 다음의 AspectJ pointcut지시자를 지원한다.

  • execution - 메소드 수행 join point를 일치시키기 위해, 이것은 Spring AOP와 작동할때 당신이 사용할 기본적인 pointcut 지시자이다.

  • within - 어떤 타입내 join point에 일치하는것을 제한(메소드의 수행은 Spring AOP를 사용할때 일치되는 타입내에서 선언된다.)

  • this - bean참조(Spring AOP프록시)가 주어진 타입의 인스턴스인 join point에 일치하는것을 제한(Spring AOP를 사용할때 메소드의 수행)

  • target - 대상 객체(프록시된 애플리케이션 객체)가 주어진 타입의 인스턴스인 join point(Spring AOP를 사용할때 메소드의 수행)에 일치하는것을 제한.

  • args - 인자가 주어진 타입의 인스턴스인 join point(Spring AOP를 사용할때 메소드의 수행)에 일치하는것을 제한.

  • @target - 수행중인 객체의 클래스가 주어진 타입의 어노테이션을 가지는 join point(Spring AOP를 사용할때 메소드의 수행)에 일치하는것을 제한.

  • @args - 전달된 실제 인자의 런타임 타입이 주어진 타입의 어노테이션을 가지는 join point(Spring AOP를 사용할때 메소드의 수행)에 일치하는것을 제한.

  • @within - 주어진 어노테이션을 가지는 타입내 join point를 일치하는것을 제한(Spring AOP를 사용할때 주어진 어노테이션을 가진 타입으로 선언된 메소드의 수행)

  • @annotation - join point(Spring AOP에서 수행되는 메소드)의 대상(subject)이 주어진 어노테이션을 가지는 join point에 일치하는것을 제한

Spring AOP는 오직 메소드 수행 join point에 일치하는 것을 제한하기 때문에, 위 pointcut지시자의 언급은 당신이 AspectJ 프로그래밍 가이드에서 찾는것보다 더 폭이 좁은 정의를 준다. 추가적으로, AspectJ 자체는 타입에 기반하는 구문을 가지고 수행시 'this' 와 'target' 두 join point는 같은 객체(메소드를 수행하는 객체)를 참조한다. Spring AOP는 프록시에 기반한 시스템이고 프록시 객체 자체('this' 로 바운드되는)와 프록시 배후의 대상 객체('target'로 바운드되는)를 구별한다.

6.2.3.2. pointcut 표현 조합하기

pointcut 표현은 '&&', '||' 그리고 '!' 를 사용하여 조합될수 있다. 이것은 이름(name)으로 pointcut표현을 참조하는 것이 가능하다. 다음 예제는 3개의 pointcut표현 즉 anyPublicOperation (메소드 수행 join point가 public메소드의 수행을 표현한다면 일치하는); inTrading (trading 모듈에서 메소드가 수행된다면 일치하는), 그리고 tradingOperation (메소드 수행이 trading 모듈내 public 메소드를 표현한다면 일치하는)을 보여준다.

    @Pointcut("execution(public * *(..))")
    private void anyPublicOperation() {}
    
    @Pointcut("within(com.xyz.someapp.trading..*")
    private void inTrading() {}
    
    @Pointcut("anyPublicOperation() && inTrading()")
    private void tradingOperation() {}

이것은 위에서 보여진것처럼 좀더 작은 명명된 컴포넌트가 없는 좀더 복잡한 pointcut표현을 빌드하기 위한 가장 좋은 예제이다. 이름(name)으로 pointcut를 참조할때, 대개의 Java 가시성(visibility) 규칙을 적용한다(당신은 같은 타입내 private pointcut, 구조내에서 protected pointcut, public pointcut등을 볼수 있다). 가시성(Visibility)은 pointcut 일치(matching)에 영향을 끼치지 않는다..

6.2.3.3. 공통 pointcut 정의 공유하기

기업용 애플리케이션으로 작업할때, 당신은 애플리케이션의 모듈과 다양한 aspect의 특정 작업 세트를 참조하길 원한다. 우리는 이러한 목적을 위해 공통적인 pointcut표현을 획득하는 "SystemArchitecture" aspect를 정의하는 것을 추천한다. 대개의 aspect는 다음과 같을것이다.

package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

  /**
   * A join point is in the web layer if the method is defined
   * in a type in the com.xyz.someapp.web package or any sub-package
   * under that.
   */
  @Pointcut("within(com.xyz.someapp.web..*)")
  public void inWebLayer() {}

  /**
   * A join point is in the service layer if the method is defined
   * in a type in the com.xyz.someapp.service package or any sub-package
   * under that.
   */
  @Pointcut("within(com.xyz.someapp.service..*)")
  public void inServiceLayer() {}

  /**
   * A join point is in the data access layer if the method is defined
   * in a type in the com.xyz.someapp.dao package or any sub-package
   * under that.
   */
  @Pointcut("within(com.xyz.someapp.dao..*)")
  public void inDataAccessLayer() {}

  /**
   * A business service is the execution of any method defined on a service
   * interface. This definition assumes that interfaces are placed in the
   * "service" package, and that implementation types are in sub-packages.
   * 
   * If you group service interfaces by functional area (for example, 
   * in packages com.xyz.someapp.abc.service and com.xyz.def.service) then
   * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
   * could be used instead.
   */
  @Pointcut("execution(* com.xyz.someapp.service.*.*(..))")
  public void businessService() {}
  
  /**
   * A data access operation is the execution of any method defined on a 
   * dao interface. This definition assumes that interfaces are placed in the
   * "dao" package, and that implementation types are in sub-packages.
   */
  @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
  public void dataAccessOperation() {}

}

aspect에 정의된 pointcut은 당신이 pointcut표현이 필요한 어디에서도 참조될수 있다. 예를 들어, 서비스 레이어를 트랜잭션 성질을 가지도록 만들기 위해서, 당신은 다음처럼 작성할수 있다.

<aop:config>
  <aop:advisor 
      pointcut="com.xyz.someapp.SystemArchitecture.businessService()"
      advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
<tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

<aop:config><aop:advisor> 태그는 Section 6.3, “Schema-based AOP support”에서 언급된다. 트랜잭션 태그는 Chapter 9, 트랜잭션 관리에서 언급된다.

6.2.3.4. 예제

Spring AOP 사용자는 수행(execution) pointcut 지시자를 사용하길 선호한다. 수행 표현의 형태는 :

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)

모든 부분은 반환하는 타입 패턴(returning type pattern - 위 조각내 ret-타입 패턴), 이름 패턴(name pattern), 그리고 파라미터 패턴(parameters pattern)은 선택적으로 제외한다. 반환하는 타입 패턴은 메소드의 반환 타입이 일치되는 join point를 위한 순서에 따라야만 한다. 가장 빈번히 당신은 어떤 반환타입과 일치하는 반환하는 타입 패턴으로 *를 사용할것이다. 전체 경로를 가지는 타입명은 메소드가 주어진 타입을 반환할때만 일치할것이다. 이름 패턴은 메소드명과 일치한다. 당신은 이름 패턴의 모든것이나 일부로 * 와일드카드를 사용할수 있다. 파라미터 패턴은 다소 좀더 복잡하다. ()는 파라미터를 가지지 않는 메소드와 일치한다. 반면에 (..)는 여러개의 파라미터와 일치한다. 패턴 (*)는 어떤 타입의 하나의 파라미터를 가지는 메소드와 일치한다. (*,String)는 두개의 파라미터를 가지는 메소드와 일치한다. 첫번째는 어떤 타입이든 될수 있고, 두번째는 String이 되어야만 한다. 좀더 많은 정보를 위해 AspectJ 프로그래밍 가이드의 Language Semantics 부분을 보라.

공통 pointcut 표현의 몇가지 예제는 아래에 있다.

  • public 메소드의 수행:

    execution(public * *(..))
  • "set"로 시작하는 이름을 가지는 메소드의 수행:

    execution(* set*(..))
  • AccountService 인터페이스가 정의한 메소드의 수행:

    execution(* com.xyz.service.AccountService.*(..))
  • 서비스 패키지내 정의된 메소드의 수행:

    execution(* com.xyz.service.*.*(..))
  • 서비스 패키지나 하위 패키지내 정의된 메소드의 수행:

    execution(* com.xyz.service..*.*(..))
  • 서비스 패키지내부의 join point(Spring AOP내 메소드 수행):

    within(com.xyz.service.*)
  • 서비스 패키지나 하위 패키지내부의 join point(Spring AOP내 메소드 수행:

    within(com.xyz.service..*)
  • 프록시가 AccountService 인터페이스를 구현하는 join point(Spring AOP내 메소드 수행):

    this(com.xyz.service.AccountService)
    'this' 는 바인딩 폼에서 좀더 공통적으로 사용된다. advice 몸체에서 사용가능한 프록시 객체를 만드는 방법을 위해 advice의 다음 부분을 보라.
  • 대상 객체가 AccountService 인터페이스를 구현하는 join point(Spring AOP내 메소드 수행):

    target(com.xyz.service.AccountService)
    'target' 은 바인딩 폼에서 좀더 공통적으로 사용된다. advice몸체에서 사용가능한 대상 객체를 만드는 방법을 위해 advice의 다음 부분을 보라.
  • 하나의 파라미터를 가지는 join point, 런타임시 전달되는 인자는 Serializable이다:

    args(java.io.Serializable)
    'args' 는 바인딩 폼에서 좀더 공통적으로 사용된다. advice몸체에서 사용가능한 메소드 인자를 만드는 방법을 위해 advice의 다음 부분을 보라.

    이 예제에 주어진 pointcut은 execution(* *(java.io.Serializable))과 다르다: args설명(version)은 런타임시 전달된 인자가 Serializable라면 일치한다. execution설명(version)은 메소드 시그너처가 Serializable 타입의 하나의 파라미터를 선언한다면 일치한다.

  • 대상 객체가 @Transactional 어노테이션을 가지는 join point(Spring AOP내 메소드 수행):

    @target(org.springframework.transaction.annotation.Transactional)
    '@target' 은 바인딩 폼에서 사용될수 있다. advice몸체에서 사용가능한 어노테이션 객체를 만드는 방법을 위해 advice의 다음 부분을 보라.
  • 대상 객체의 선언된 타입이 @Transactional 어노테이션을 가지는 join point(Spring AOP내 메소드 수행):

    @within(org.springframework.transaction.annotation.Transactional)
    '@within' 은 바인딩 폼에서 사용될수 있다. advice몸체에서 사용가능한 어노테이션 객체를 생성하는 방법을 위해 advice의 다음 부분을 보라.
  • 수행(executing) 메소드가 @Transactional 어노테이션을 가지는 join point(Spring AOP내 메소드 수행):

    @annotation(org.springframework.transaction.annotation.Transactional)
    '@annotation' 은 바인딩 폼에서 사용될수 있다. advice몸체에서 사용가능한 어노테이션 객체를 생성하는 방법을 위해 advice의 다음 부분을 보라.
  • 하나의 파라미터를 가지고 전달된 인자의 런타임 타입이 @Classified 어노테이션을 가지는 join point(Spring AOP내 메소드 수행):

    @args(com.xyz.security.Classified)
    '@args' 는 바인딩 폼에서 사용될수 있다. advice몸체에서 사용가능한 어노테이션 객체를 생성하는 방법을 위해 advice의 다음 부분을 보라.

6.2.4. advice 선언하기

advice는 pointcut표현과 관련되고 pointcut이 일치하는 before, after 또는 around 메소드 수행을 작동시킨다. pointcut 표현은 명명된 pointcut에 대한 간단한 참조나 선언된 pointcut표현이 될것이다.

6.2.4.1. Before advice

Before advice는 @Before 어노테이션을 사용하여 aspect내 선언된다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

  @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doAccessCheck() {
    // ...
  }

}

적절한 pointcut표현을 사용한다면, 우리는 위 예제를 다시 작성할수 있다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

  @Before("execution(* com.xyz.myapp.dao.*.*(..))")
  public void doAccessCheck() {
    // ...
  }

}

6.2.4.2. After 반환하는(returning) advice

After 반환하는(returning) advice는 일치되는 메소드 수행이 대개 반환할때 수행한다. 이것은 @AfterReturning 어노테이션을 사용하여 선언된다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doAccessCheck() {
    // ...
  }

}
노트: 이것은 물론 같은 aspect내부에 여러개의 advice선언과 다른 멤버을 가지는 것이 가능하다. 우리는 논의내 이슈에 집중하기 위한 예제에서 하나의 advice선언을 보여준다.

언젠가 당신은 반환되는 실제값을 위해 advice몸체에 접근할 필요가 있다. 당신은 반환값을 바인딩하는 @AfterReturning 형태를 사용할수 있다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    returning="retVal")
  public void doAccessCheck(Object retVal) {
    // ...
  }
  
}

returning속성에서 사용된 이름은 advice메소드내 파라미터의 이름과 일치한다. 메소드 수행이 반환할때, 반환값은 일치하는 인자값으로 advice메소드에 전달될것이다. returning절이 명시된 타입(Object의 경우, 어떠한 반환값도 일치할것이다.)의 값을 반환하는 메소드 수행에만 일치하도록 제한한다.

6.2.4.3. After throwing advice

After throwing advice는 일치되는 메소드 수행이 예외를 던져서 빠져나갈때 수행한다. 이것은 @AfterThrowing 어노테이션을 사용하여 선언된다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doRecoveryActions() {
    // ...
  }

}

종종 당신은 주어진 타입의 예외가 던져질때만 수행할 advice를 원하고 advice몸체에서 던져진 예외에 접근할 필요가 있다. 던져진 예외를 advice파라미터에 일치시키는 것을 제한하고 바인드하기 위해 throwing 속성을 사용하라. (원한다면, 다른 예외타입으로 Throwable를 사용하라.)

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    throwing="ex")
  public void doRecoveryActions(DataAccessException ex) {
    // ...
  }

}

throwing 속성에서 사용된 이름은 advice메소드내 파라미터의 이름과 일치해야만 한다. 메소드 수행이 예외를 던져서 빠져나갈때, 예외는 일치하는 인자값으로 advice메소드에 전달될것이다. throwing절은 명시된 타입(이 경우 DataAccessException)의 예외를 던지는 메소드 수행을 위해서만 일치하는 것을 제한한다.

6.2.4.4. After (finally) advice

After (finally) advice는 일치되는 메소드 수행이 아무리 빠져나가더라도 수행한다. 이것은 @After 어노테이션을 사용하여 선언된다. After advice는 일반적이고 예외를 반환하는 상태를 다루기 위해서 준비되어야만 한다. 이것은 자원을 풀어주기 위해 사용된다.

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

  @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doReleaseLock() {
    // ...
  }

}

6.2.4.5. Around advice

advice의 마지막 종류는 around advice이다. Around advice는 일치되는 메소드 수행 "도처에(around)" 수행한다. 이것은 메소드를 수행하는 이전과 이후에 작업하는 기회를 가진다. Around advice는 쓰레드에 안전한 방법(예를 들어 타이머를 시작하고 종료하는)으로 메소드를 수행하기 전과 후에 상태를 공유할 필요가 있다면 종종 사용된다. 언제나 당신의 요구사항에 만족하는 최소한의 advice 형태를 사용하라(이를테면, 간단한 before advice가 그렇게 한다면 around advice를 사용하지 말라).

Around advice는 @Around 어노테이션을 사용하여 선언된다. advice메소드의 첫번째 파라미터는 ProceedingJoinPoint 타입이어야만 한다. advice의 몸체내부에서, ProceedingJoinPointproceed()를 호출하는 것은 기초적인 메소드가 수행되도록 만든다. proceed 메소드는 Object[]로 전달되는 것을 호출할것이다. 이 배열의 값은 처리할때 메소드 수행에 인자로 사용될것이다.

The behavior of proceed when called with an Object[] is a little different than the behavior of proceed for around advice compiled by the AspectJ compiler. For around advice written using the traditional AspectJ language, the number of arguments passed to proceed must match the number of arguments passed to the around advice (not the number of arguments taken by the underlying join point), and the value passed to proceed in a given argument position supplants the original value at the join point for the entity the value was bound to. (Don't worry if this doesn't make sense right now!). The approach taken by Spring is simpler and a better match to its proxy-based, execution only semantics. You only need to be aware of this difference if you compiling @AspectJ aspects written for Spring and using proceed with arguments with the AspectJ compiler and weaver. There is a way to write such aspects that is 100% compatible across both Spring AOP and AspectJ, and this is discussed in the following section on advice parameters.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

  @Around("com.xyz.myapp.SystemArchitecture.businessService()")
  public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
  }

}

The value returned by the around advice will be the return value seen by the caller of the method. A simple caching aspect for example could return a value from a cache if it has one, and invoke proceed() if it does not. Note that proceed may be invoked once, many times, or not at all within the body of the around advice, all of these are quite legal.

6.2.4.6. Advice parameters

Spring 2.0 offers fully typed advice - meaning that you declare the parameters you need in the advice signature (as we saw for the returning and throwing examples above) rather than work with Object[] arrays all the time. We'll see how to make argument and other contextual values available to the advice body in a moment. First let's take a look at how to write generic advice that can find out about the method the advice is currently advising.

6.2.4.6.1. Access to the current JoinPoint

Any advice method may declare as its first parameter, a parameter of type org.aspectj.lang.JoinPoint (please note that around advice is required to declare a first parameter of type ProceedingJoinPoint, which is a subclass of JoinPoint. The JoinPoint interface provides a number of useful methods such as getArgs() (returns the method arguments), getThis() (returns the proxy object), getTarget() (returns the target object), getSignature() (returns a description of the method that is being advised) and toString() (prints a useful description of the method being advised). Please do consult the Javadocs for full details.

6.2.4.6.2. Passing parameters to advice

We've already seen how to bind the returned value or exception value (using after returning and after throwing advice). To make argument values available to the advice body, you can use the binding form of args. If a parameter name is used in place of a type name in an args expression, then the value of the corresponding argument will be passed as the parameter value when the advice is invoked. An example should make this clearer. Suppose you want to advise the execution of dao operations that take an Account object as the first parameter, and you need access to the account in the advice body. You could write the following:

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + 
        "args(account,..)")
public void validateAccount(Account account) {
  // ...
}

The args(account,..) part of the pointcut expression serves two purposes: firstly it restricts matching to only those method executions where the method takes at least one parameter, and the argument passed to that parameter is an instance of Account; secondly, it makes the actual Account object available to the advice via the account parameter.

Another way of writing this is to declare a pointcut that "provides" the Account object value when it matches a join point, and then just refer to the named pointcut from the advice. This would look as follows:

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + 
          "args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
  // ...
}

The interested reader is once more refered to the AspectJ programming guide for more details.

The proxy object (this), target object (target), and annotations (@within, @target, @annotation, @args) can all be bound in a similar fashion. The following example shows how you could match the execution of methods annotated with an @Auditable annotation, and extract the audit code.

First the definition of the @Auditable annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
	AuditCode value();
}

And then the advice that matches the execution of @Auditable methods:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " + 
        "@annotation(auditable)")
public void audit(Auditable auditable) {
  AuditCode code = auditable.value();
  // ...
}
6.2.4.6.3. Determining argument names

The parameter binding in advice invocations relies on matching names used in pointcut expressions to declared parameter names in (advice and pointcut) method signatures. Parameter names are not available through Java reflection, so Spring AOP uses the following strategies to determine parameter names:

  1. If the parameter names have been specified by the user explicitly, then the specified parameter names are used: both the advice and the pointcut annotations have an optional "argNames" attribute which can be used to specify the argument names of the annotated method - these argument names are available at runtime. For example:

    @Before(
       value="com.xyz.lib.Pointcuts.anyPublicMethod() && " + 
             "@annotation(auditable)",
       argNames="auditable")
    public void audit(Auditable auditable) {
      AuditCode code = auditable.value();
      // ...
    }
    If an @AspectJ aspect has been compiled by the AspectJ compiler (ajc) then there is no need to add the argNames attribute as the compiler will do this automatically.
  2. Using the 'argNames' attribute is a little clumsy, so if the 'argNames' attribute has not been specified, then Spring AOP will look at the debug information for the class and try to determine the parameter names from the local variable table. This information will be present as long as the classes have been compiled with debug information ('-g:vars' at a minimum). The consequences of compiling with this flag on are: (1) your code will be slightly easier to understand (reverse engineer), (2) the class file sizes will be very slightly bigger (typically inconsequential), (3) the optimization to remove unused local variables will not be applied by your compiler. In other words, uou should encounter no difficulties building with this flag on.

  3. If the code has been compiled without the necessary debug information, then Spring AOP will attempt to deduce the pairing of binding variables to parameters (for example, if only one variable is bound in the pointcut expression, and the advice method only takes one parameter, the pairing is obvious!). If the binding of variables is ambiguous given the available information, then an AmbiguousBindingException will be thrown.

  4. If all of the above strategies fail then an IllegalArgumentException will be thrown.

6.2.4.6.4. Proceeding with arguments

We remarked earlier that we would describe how to write a proceed call with arguments that works consistently across Spring AOP and AspectJ. The solution is simply to ensure that the advice signature binds each of the method parameters in order. For example:

@Around("execution(List<Account> find*(..)) &&" +
        "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")		
public Object preProcessQueryPattern(ProceedingJoinPoint pjp, String accountHolderNamePattern)
throws Throwable {
  String newPattern = preProcess(accountHolderNamePattern);
  return pjp.proceed(new Object[] {newPattern});
}        

In many cases you will be doing this binding anyway (as in the example above).

6.2.4.7. Advice ordering

What happens when multiple pieces of advice all want to run at the same join point? Spring AOP follows the same precedence rules as AspectJ to determine the order of advice execution. The highest precedence advice runs first "on the way in" (so given two pieces of before advice, the one with highest precedence runs first). "On the way out" from a join point, the highest precedence advice runs last (so given two pieces of after advice, the one with the highest precedence will run second). For advice defined within the same aspect, precedence is established by declaration order. Given the aspect:

@Aspect
public class AspectWithMultipleAdviceDeclarations {

  @Pointcut("execution(* foo(..))")
  public void fooExecution() {}
  
  @Before("fooExecution()")
  public void doBeforeOne() {
    // ...
  }
  
  @Before("fooExecution()")
  public void doBeforeTwo() {
    // ...
  }
  
  @AfterReturning("fooExecution()")
  public void doAfterOne() {
    // ...
  }

  @AfterReturning("fooExecution()")
  public void doAfterTwo() {
    // ...
  }

}

then for any execution of a method named foo, the doBeforeOne, doBeforeTwo, doAfterOne, and doAfterTwo advice methods all need to run. The precedence rules are such that the advice will execute in declaration order. In this case the execution trace would be:

doBeforeOne
doBeforeTwo
foo
doAfterOne
doAfterTwo

In other words doBeforeOne has precedence over doBeforeTwo, because it was defined before doBeforeTwo, and doAfterTwo has precedence over doAfterOne because it was defined after doAfterOne. It's easiest just to remember that advice runs in declaration order ;) - see the AspectJ Programming Guide for full details.

When two pieces of advice defined in different aspects both need to run at the same join point, then unless you specify otherwise the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by implementing the org.springframework.core.Ordered interface in the aspect class. Given two aspects, the aspect returning the lower value from Ordered.getValue() has the higher precedence.

6.2.5. Introductions

Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.

An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name). For example, given an interface UsageTracked, and an implementation of that interface DefaultUsageTracked, the following aspect declares that all implementors of service interfaces also implement the UsageTracked interface. (In order to expose statistics via JMX for example).

@Aspect
public class UsageTracking {

  @DeclareParents(value="com.xzy.myapp.service.*+",
                  defaultImpl=DefaultUsageTracked.class)
  public static UsageTracked mixin;
  
  @Before("com.xyz.myapp.SystemArchitecture.businessService() &&" +
          "this(usageTracked)")
  public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
  }
  
}

The interface to be implemented is determined by the type of the annotated field. The value attribute of the @DeclareParents annotation is an AspectJ type pattern :- any bean of a matching type will implement the UsageTracked interface. Note that in the before advice of the above example, service beans can be directly used as implementations of the UsageTracked interface. If accessing a bean programmatically you would write the following:

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

6.2.6. Aspect instantiation models

This is an advanced topic...

By default there will be a single instance of each aspect within the application context. AspectJ calls this the singleton instantiation model. It is possible to define aspects with alternate lifecycles :- Spring supports AspectJ's perthis and pertarget instantiation models (percflow, percflowbelow, and pertypewithin are not currently supported).

A "perthis" aspect is declared by specifying a perthis clause in the @Aspect annotation. Let's look at an example, and then we'll explain how it works.

@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {

  private int someState;
	
  @Before(com.xyz.myapp.SystemArchitecture.businessService())
  public void recordServiceUsage() {
    // ...
  }
  	
}

The effect of the perthis clause is that one aspect instance will be created for each unique service object executing a business service (each unique object bound to 'this' at join points matched by the pointcut expression). The aspect instance is created the first time that a method is invoked on the service object. The aspect goes out of scope when the service object goes out of scope. Before the aspect instance is created, none of the advice within it executes. As soon as the aspect instance has been created, the advice declared within it will execute at matched join points, but only when the service object is the one this aspect is associated with. See the AspectJ programming guide for more information on per-clauses.

The 'pertarget' instantiation model works in exactly the same way as perthis, but creates one aspect instance for each unique target object at matched join points.

6.2.7. Example

Now that you've seen how all the constituent parts work, let's put them together to do something useful!

The execution of business services can sometimes fail due to concurrency issues (for example, deadlock loser). If the operation is retried, it is quite likely it will succeed next time round. For business services where it is appropriate to retry in such conditions (idempotent operations that don't need to go back to the user for conflict resolution), we'd like to transparently retry the operation to avoid the client seeing a PessimisticLockingFailureException. This is a requirement that clearly cuts across multiple services in the service layer, and hence is ideal for implementing via an aspect.

Because we want to retry the operation, we'll need to use around advice so that we can call proceed multiple times. Here's how the basic aspect implementation looks:

@Aspect
public class ConcurrentOperationExecutor implements Ordered {
   
   private static final int DEFAULT_MAX_RETRIES = 2;

   private int maxRetries = DEFAULT_MAX_RETRIES;
   private int order = 1;

   public void setMaxRetries(int maxRetries) {
      this.maxRetries = maxRetries;
   }
   
   public int getOrder() {
      return this.order;
   }
   
   public void setOrder(int order) {
      this.order = order;
   }
   
   @Around("com.xyz.myapp.SystemArchitecture.businessService()")
   public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
      int numAttempts = 0;
      PessimisticLockingFailureException lockFailureException;
      do {
         numAttempts++;
         try { 
            return pjp.proceed();
         }
         catch(PessimisticLockingFailureException ex) {
            lockFailureException = ex;
         }
      }
      while(numAttempts <= this.maxRetries);
      throw lockFailureException;
   }

}

Note that the aspect implements the Ordered interface so we can set the precedence of the aspect higher than the transaction advice (we want a fresh transaction each time we retry). The maxRetries and order properties will both be configured by Spring. The main action happens in the doConcurrentOperation around advice. Notice that for the moment we're applying the retry logic to all businessService()s. We try to proceed, and if we fail with an PessimisticLockingFailureException we simply try again unless we have exhausted all of our retry attempts.

The corresponding Spring configuration is:

<aop:aspectj-autoproxy/>

<bean id="concurrentOperationExecutor"
  class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
     <property name="maxRetries" value="3"/>
     <property name="order" value="100"/>  
</bean>

To refine the aspect so that it only retries idempotent operations, we might define an Idempotent annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
  // marker annotation
}

and use the annotation to annotate the implementation of service operations. The change to the aspect to only retry idempotent operations simply involves refining the pointcut expression so that only @Idempotent operations match:

@Around("com.xyz.myapp.SystemArchitecture.businessService() && " + 
        "@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
  ...	
}

6.3. Schema-based AOP support

If you are unable to use Java 5, or simply prefer an XML-based format, then Spring 2.0 also offers support for defining aspects using the new "aop" namespace tags. The exact same pointcut expressions and advice kinds are supported as when using the @AspectJ style, hence in this section we will focus on the new syntax and refer the reader to the discussion in the previous section (Section 6.2, “@AspectJ 지원”) for a understanding of writing pointcut expressions and the binding of advice parameters.

To use the aop namespace tags described in this section, you need to import the spring-aop schema as described in Appendix A, XML 스키마-기반 설정. See Section A.2.6, “aop 스키마” for how to import the tags in the aop namespace.

Within your Spring configurations, all aspect and advisor elements must be placed within an <aop:config> element (you can have more than one <aop:config> element in an application context configuration). An <aop:config> element can contain pointcut, advisor, and aspect elements (and these must be declared in that order).

[Warning]Warning

The <aop:config> style of configuration makes heavy use of Spring's auto-proxying mechanism. This can cause issues (such as advice not being woven) if you are already using explicit auto-proxying via the use of BeanNameAutoProxyCreator or suchlike. The recommended usage pattern is to use either just the <aop:config> style, or just the AutoProxyCreator style.

6.3.1. Declaring an aspect

Using the schema support, an aspect is simply a regular Java object defined as a bean in your spring application context. The state and behavior is captured in the fields and methods of the object, and the pointcut and advice information is captured in the XML.

An aspect is declared using the <aop:aspect> element, and the backing bean is referenced using the ref attribute:

<aop:config>
  <aop:aspect id="myAspect" ref="aBean">
    ...
  </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
  ...
</bean>

The bean backing the aspect ("aBean" in this case) can of course be configured and dependency injected just like any other Spring bean.

6.3.2. Declaring a pointcut

A pointcut can be declared inside an aspect, in which case it is visible only within that aspect. A pointcut can also be declared directly inside an <aop:config> element, enabling the pointcut definition to be shared across several aspects and advisors.

A pointcut representing the execution of any business service in the service layer could be defined as follows:

<aop:config>

  <aop:pointcut id="businessService" 
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

</aop:config>

Note that the pointcut expression itself is using the same AspectJ pointcut expression language as described in Section 6.2, “@AspectJ 지원”. If you are using the schema based declaration style with Java 5, you can refer to named pointcuts defined in types (@Aspects) within the pointcut expression, but this feature is not available on JDK 1.4 and below (it relies on the Java 5 specific AspectJ reflection APIs). On JDK 1.5 therefore, another way of defining the above pointcut would be:

<aop:config>

  <aop:pointcut id="businessService" 
        expression="com.xyz.myapp.SystemArchitecture.businessService()"/>

</aop:config>

Assuming you have a SystemArchitecture aspect as described in Section 6.2.3.3, “공통 pointcut 정의 공유하기”.

Declaring a pointcut inside an aspect is very similar to declaring a top-level pointcut:

<aop:config>

  <aop:aspect id="myAspect" ref="aBean">

    <aop:pointcut id="businessService" 
          expression="execution(* com.xyz.myapp.service.*.*(..))"/>
          
    ...
    
  </aop:aspect>

</aop:config>

When combining pointcut sub-expressions, '&&' is awkward within an XML document, and so the keywords 'and', 'or' and 'not' can be used in place of '&&', '||' and '!' respectively.

Note that pointcuts defined in this way are referred to by their XML id, and cannot define pointcut parameters. The named pointcut support in the schema based definition style is thus more limited than that offered by the @AspectJ style.

6.3.3. Declaring advice

The same five advice kinds are supported as for the @AspectJ style, and they have exactly the same semantics.

6.3.3.1. Before advice

Before advice runs before a matched method execution. It is declared inside an <aop:aspect> using the <aop:before> element.

<aop:aspect id="beforeExample" ref="aBean">

    <aop:before 
      pointcut-ref="dataAccessOperation" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

Here dataAccessOperation is the id of a pointcut defined at the top (<aop:config>) level. To define the pointcut inline instead, replace the pointcut-ref attribute with a pointcut attribute:

<aop:aspect id="beforeExample" ref="aBean">

    <aop:before 
      pointcut="execution(* com.xyz.myapp.dao.*.*(..))" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

As we noted in the discussion of the @AspectJ style, using named pointcuts can significantly improve the readability of your code.

The method attribute identifies a method (doAccessCheck) that provides the body of the advice. This method must be defined for the bean referenced by the aspect element containing the advice. Before a data access operation is executed (a method execution join point matched by the pointcut expression), the "doAccessCheck" method on the aspect bean will be invoked.

6.3.3.2. After returning advice

After returning advice runs when a matched method execution completes normally. It is declared inside an <aop:aspect> in the same way as before advice. For example:

<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning 
      pointcut-ref="dataAccessOperation" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

Just as in the @AspectJ style, it is possible to get hold of the return value within the advice body. Use the returning attribute to specify the name of the parameter to which the return value should be passed:

<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning 
      pointcut-ref="dataAccessOperation"
      returning="retVal" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

The doAccessCheck method must declare a parameter named retVal. The type of this parameter constrains matching in the same way as described for @AfterReturning. For example, the method signature may be declared as:

public void doAccessCheck(Object retVal) {...

6.3.3.3. After throwing advice

After throwing advice executes when a matched method execution exits by throwing an exception. It is declared inside an <aop:aspect> using the after-throwing element:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
      pointcut-ref="dataAccessOperation" 
      method="doRecoveryActions"/>
          
    ...
    
</aop:aspect>

Just as in the @AspectJ style, it is possible to get hold of the thrown exception within the advice body. Use the throwing attribute to specify the name of the parameter to which the exception should be passed:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing 
      pointcut-ref="dataAccessOperation"
      thowing="dataAccessEx" 
      method="doRecoveryActions"/>
          
    ...
    
</aop:aspect>

The doRecoveryActions method must declare a parameter named dataAccessEx. The type of this parameter constrains matching in the same way as described for @AfterThrowing. For example, the method signature may be declared as:

public void doRecoveryActions(DataAccessException dataAccessEx) {...

6.3.3.4. After (finally) advice

After (finally) advice runs however a matched method execution exits. It is declared using the after element:

<aop:aspect id="afterFinallyExample" ref="aBean">

    <aop:after
      pointcut-ref="dataAccessOperation" 
      method="doReleaseLock"/>
          
    ...
    
</aop:aspect>

6.3.3.5. Around advice

The final kind of advice is around advice. Around advice runs "around" a matched method execution. It has the opportunity to do work both before and after the method executes, and to determine when, how, and even if, the method actually gets to execute at all. Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer for example). Always use the least powerful form of advice that meets your requirements (i.e. don't use around advice if simple before advice would do).

Around advice is declared using the aop:around element. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed() on the ProceedingJoinPoint causes the underlying method to execute. The proceed method may also be calling passing in an Object[] - the values in the array will be used as the arguments to the method execution when it proceeds. See Section 6.2.4.5, “Around advice” for notes on calling proceed with an Object[].

<aop:aspect id="aroundExample" ref="aBean">

    <aop:around
      pointcut-ref="businessService" 
      method="doBasicProfiling"/>
          
    ...
    
</aop:aspect>

The implementation of the doBasicProfiling advice would be exactly the same as in the @AspectJ example (minus the annotation of course):

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}

6.3.3.6. Advice parameters

The schema based declaration style supports fully typed advice in the same way as described for the @AspectJ support - by matching pointcut parameters by name against advice method parameters. See Section 6.2.4.6, “Advice parameters” for details.

If you wish to explicity specify argument names for the advice methods (not relying on either of the detection strategies previously described) then this is done using the arg-names attribute of the advice element. For example:

<aop:before
  pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
  method="audit"
  arg-names="auditable"/>

The arg-names attribute accepts a comma-delimited list of parameter names.

Find below a slightly more involved example of the XSD-based approach that illustrates some around advice used in conjunction with a number of strongly typed parameters.

First off, the service interface and implementation of said interface that will be being advised:

package x.y.service;

public interface FooService {

   Foo getFoo(String fooName, int age);
}

// the attendant implementation (defined in another file of course)

public class DefaultFooService implements FooService {

   public Foo getFoo(String name, int age) {
      return new Foo(name, age);
   }
}

Next up is the (admittedly simple) aspect. Notice the fact that the profile(..) method accepts a number of strongly-typed parameters, the first of which happens to be the join point used to proceed with the method call: the presence of this parameter is an indication that the profile(..) is to be used as around advice:

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class SimpleProfiler {

   public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
      StopWatch clock = new StopWatch(
            "Profiling for '" + name + "' and '" + age + "'");
      try {
         clock.start(call.toShortString());
         return call.proceed();
      } finally {
         clock.stop();
         System.out.println(clock.prettyPrint());
      }
   }
}

Finally, here is the XML configuration that is required to effect the execution of the above advice for a particular joinpoint:

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      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.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

   <!-- this is the object that will be proxied by Spring's AOP infrastructure -->
   <bean id="fooService" class="x.y.service.DefaultFooService"/>

   <!-- this is the actual advice itself -->
   <bean id="profiler" class="x.y.SimpleProfiler"/>

   <aop:config>
      <aop:aspect ref="profiler">

         <aop:pointcut id="theExecutionOfSomeFooServiceMethod"
                    expression="execution(* x.y.service.FooService.getFoo(String,int))
                    and args(name, age)"/>

         <aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
                  method="profile"/>

      </aop:aspect>
   </aop:config>

</beans>

If we had the following driver script, we would get output something like this on standard output:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.FooService;

public final class Boot {

   public static void main(final String[] args) throws Exception {
      BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
      FooService foo = (FooService) ctx.getBean("fooService");
      foo.getFoo("Pengo", 12);
   }
}
StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0
-----------------------------------------
ms     %     Task name
-----------------------------------------
00000  ?  execution(getFoo)

6.3.3.7. Advice ordering

When multiple advice needs to execute at the same join point (executing method) the ordering rules are as described in Section 6.2.4.7, “Advice ordering”. The precedence between aspects is determined by implementing the Ordered interface on the bean backing the aspect.

6.3.4. Introductions

Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.

An introduction is made using the aop:declare-parents element inside an aop:aspect This element is used to declare that matching types have a new parent (hence the name). For example, given an interface UsageTracked, and an implementation of that interface DefaultUsageTracked, the following aspect declares that all implementors of service interfaces also implement the UsageTracked interface. (In order to expose statistics via JMX for example.)

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

  <aop:declare-parents
      types-matching="com.xzy.myapp.service.*+",
      implement-interface="UsageTracked"
      default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
  
  <aop:before
    pointcut="com.xyz.myapp.SystemArchitecture.businessService()
              and this(usageTracked)"
    method="recordUsage"/>
  
</aop:aspect>

The class backing the usageTracking bean would contain the method:

public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
}

The interface to be implemented is determined by implement-interface attribute. The value of the types-matching attribute is an AspectJ type pattern :- any bean of a matching type will implement the UsageTracked interface. Note that in the before advice of the above example, service beans can be directly used as implementations of the UsageTracked interface. If accessing a bean programmatically you would write the following:

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

6.3.5. Aspect instantiation models

The only supported instantiation model for schema-defined aspects is the singleton model. Other instantiation models may be supported in future releases.

6.3.6. Advisors

The concept of "advisors" is brought forward from the AOP support defined in Spring 1.2 and does not have a direct equivalent in AspectJ. An advisor is like a small self-contained aspect that has a single piece of advice. The advice itself is represented by a bean, and must implement one of the advice interfaces described in Section 7.3.2, “Spring내 Advice 타입”. Advisors can take advantage of AspectJ pointcut expressions though.

Spring 2.0 supports the advisor concept with the <aop:advisor> element. You will most commonly see it used in conjunction with transactional advice, which also has its own namespace support in Spring 2.0. Here's how it looks:

<aop:config>

  <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

  <aop:advisor 
      pointcut-ref="businessService"
      advice-ref="tx-advice"/>
      
</aop:config>

<tx:advice id="tx-advice">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

As well as the pointcut-ref attribute used in the above example, you can also use the pointcut attribute to define a pointcut expression inline.

To define the precedence of an advisor so that the advice can participate in ordering, use the order attribute to define the Ordered value of the advisor.

6.3.7. Example

Let's see how the concurrent locking failure retry example from Section 6.2.7, “Example” looks when rewritten using the schema support.

The execution of business services can sometimes fail due to concurrency issues (for example, deadlock loser). If the operation is retried, it is quite likely it will succeed next time round. For business services where it is appropriate to retry in such conditions (idempotent operations that don't need to go back to the user for conflict resolution), we'd like to transparently retry the operation to avoid the client seeing a PessimisticLockingFailureException. This is a requirement that clearly cuts across multiple services in the service layer, and hence is ideal for implementing via an aspect.

Because we want to retry the operation, we'll need to use around advice so that we can call proceed multiple times. Here's how the basic aspect implementation looks (it's just a regular Java class using the schema support):

public class ConcurrentOperationExecutor implements Ordered {
   
   private static final int DEFAULT_MAX_RETRIES = 2;

   private int maxRetries = DEFAULT_MAX_RETRIES;
   private int order = 1;

   public void setMaxRetries(int maxRetries) {
      this.maxRetries = maxRetries;
   }
   
   public int getOrder() {
      return this.order;
   }
   
   public void setOrder(int order) {
      this.order = order;
   }
   
   public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
      int numAttempts = 0;
      PessimisticLockingFailureException lockFailureException;
      do {
         numAttempts++;
         try { 
            return pjp.proceed();
         }
         catch(PessimisticLockingFailureException ex) {
            lockFailureException = ex;
         }
      }
      while(numAttempts <= this.maxRetries);
      throw lockFailureException;
   }

}

Note that the aspect implements the Ordered interface so we can set the precedence of the aspect higher than the transaction advice (we want a fresh transaction each time we retry). The maxRetries and order properties will both be configured by Spring. The main action happens in the doConcurrentOperation around advice method. We try to proceed, and if we fail with a PessimisticLockingFailureException we simply try again unless we have exhausted all of our retry attempts.

This class is identical to the one used in the @AspectJ example, but with the annotations removed.

The corresponding Spring configuration is:

<aop:config>

  <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">

    <aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>
       
    <aop:around
       pointcut-ref="idempotentOperation"
       method="doConcurrentOperation"/>
  
  </aop:aspect>

</aop:config>

<bean id="concurrentOperationExecutor"
  class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
     <property name="maxRetries" value="3"/>
     <property name="order" value="100"/>  
</bean>

Notice that for the time being we assume that all business services are idempotent. If this is not the case we can refine the aspect so that it only retries genuinely idempotent operations, by introducing an Idempotent annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
  // marker annotation
}

and using the annotation to annotate the implementation of service operations. The change to the aspect to only retry idempotent operations simply involves refining the pointcut expression so that only @Idempotent operations match:

  <aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service.*.*(..)) and
                    @annotation(com.xyz.myapp.service.Idempotent)"/>

6.4. Choosing which AOP declaration style to use

Once you have decided that an aspect is the best approach for implementing a given requirement, how do you decide between using Spring AOP or AspectJ, and between the Aspect language (code) style, @AspectJ declaration style, and XML? These decisions are influenced by a number of factors including application requirements, development tools, and team familiarity with AOP.

6.4.1. Spring AOP or full AspectJ?

Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, then Spring AOP is the right choice. If you need to advise domain objects, or any other object not managed by the Spring container, then you will need to use AspectJ. You will also need to use AspectJ if you wish to advise join points other than simple method executions (for example, call join points, field get or set join points, and so on).

When using AspectJ, you have the choice of the AspectJ language syntax (also known as the "code style") or the @AspectJ annotation style. If aspects play a large role in your design, and you are able to use the AspectJ Development Tools (AJDT) in Eclipse, then the AspectJ language syntax is the preferred option :- it's cleaner and simpler because the language was purposefully designed for writing aspects. If you are not using Eclipse, or have only a few aspects that do not play a major role in your application, then you may want to consider using the @AspectJ style and sticking with a regular Java compilation in your IDE, and adding an aspect weaving (linking) phase to your build scripts.

6.4.2. @AspectJ or XML for Spring AOP?

If you have chosen to use Spring AOP, then you have a choice of @AspectJ or XML style. On balance we recommend use of the @AspectJ style if you are using Java 5. Clearly if you are not running on Java 5, then the XML style is the best choice. Details of the trade-offs between the XML and @AspectJ styles are discussed below.

The XML style will be most familiar to existing Spring users. It can be used with any JDK level (referring to named pointcuts from within pointcut expressions does still require Java 5 though) and is backed by genuine POJOs. When using AOP as a tool to configure enterprise services (a good test is whether you consider the pointcut expression to be a part of your configuration you might want to change independently) then XML can be a good choice. With the XML style it is arguably clearer from your configuration what aspects are present in the system.

The XML style has two disadvantages. Firstly it does not fully encapsulate the implementation of the requirement it addresses in a single place. The DRY principle says that there should be a single, unambiguous, authoritative representation of any piece of knowledge within a system. When using the XML style, the knowledge of how a requirement is implemented is split across the declaration of the backing bean class, and the XML in the configuration file. When using the @AspectJ style there is a single module - the aspect - in which this information is encapsulated. Secondly, the XML style is more limited in what in can express than the @AspectJ style: only the "singleton" aspect instantiation model is supported, and it is not possible to combine named pointcuts declared in XML. For example, in the @AspectJ style we can write something like:

  @Pointcut(execution(* get*()))
  public void propertyAccess() {}

  @Pointcut(execution(org.xyz.Account+ *(..))
  public void operationReturningAnAccount() {}

  @Pointcut(propertyAccess() && operationReturningAnAccount())
  public void accountPropertyAccess() {}

In the XML style I can declare the first two pointcuts:

  <aop:pointcut id="propertyAccess" 
      expression="execution(* get*())"/>

  <aop:pointcut id="operationReturningAnAccount" 
      expression="execution(org.xyz.Account+ *(..))"/>

but I cannot define the accountPropertyAccess pointcut by combining these definitions.

The @AspectJ style supports additional instantiation models, and richer pointcut composition. It has the advantage of keeping the aspect as a modular unit. It also has the advantage the @AspectJ aspects can be understood both by Spring AOP and by AspectJ - so if you later decide you need the capabilities of AspectJ to implement additional requirements then it is very easy to migrate to an AspectJ based approach. On balance we prefer the @AspectJ style whenever you have aspects that do more than simple "configuration" of enterprise services.

6.5. Mixing aspect types

It is perfectly possible to mix @AspectJ style aspects using the autoproxying support, schema-defined <aop:aspect> aspects, <aop:advisor> declared advisors and even proxies and interceptors defined using the Spring 1.2 style in the same configuration. All of these are implemented using the same underlying support mechanism and will co-exist without any difficulty.

6.6. Proxying mechanisms

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so. However, there are some issues to consider:

  • Final methods cannot be advised, as they cannot be overriden

  • You will need the CGLIB 2 binaries on your classpath, whereas dynamic proxies are available with the JDK

To force the use of CGLIB proxies set the value of the proxy-target-class attribute of the <aop:config> element to true:

<aop:config proxy-target-class="true">
	
	...
	
</aop:config>

To force CGLIB proxying when using the @AspectJ autoproxy support, set the proxy-target-class attribute of the <aop:aspectj-autoproxy> element as follows:

<aop:aspectj-autoproxy proxy-target-class="true"/>

6.7. Programmatic creation of @AspectJ Proxies

In addition to declaring aspects in your configuration using either <aop:config> or <aop:aspectj-autoproxy> it is also possible programmatically to create proxies that advise target objects. For the full details of Spring's AOP API, see the next chapter. Here we want to focus on the ability to automatically create proxies using @AspectJ aspects.

The class org.springframework.aop.aspectj.annotation.AspectJProxyFactory can be used to create a proxy for a target object that is advised by one or more @AspectJ aspects. Basic usage for this class is very simple, as illustrated below. See the Javadocs for full information.

// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); 

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);	

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();

6.8. Using AspectJ with Spring applications

Everything we've covered so far in this chapter is pure Spring AOP. In this section, we're going to look at how you can use the AspectJ compiler/weaver instead of or in addition to Spring AOP if your needs go beyond the facilities offered by Spring AOP alone.

Spring ships with a small AspectJ aspect library (it's available standalone in your distribution as spring-aspects.jar, you'll need to add this to your classpath to use the aspects in it). Section 6.8.1, “Using AspectJ to dependency inject domain objects with Spring” and Section 6.8.2, “Other Spring aspects for AspectJ” discuss the content of this library and how you can use it. Section 6.8.3, “Configuring AspectJ aspects using Spring IoC” discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally, Section 6.8.4, “Using AspectJ Load-time weaving (LTW) with Spring applications” provides an introduction to load-time weaving for Spring applications using AspectJ.

6.8.1. Using AspectJ to dependency inject domain objects with Spring

The Spring container instantiates and configures beans defined in your application context. It is also possible to ask a bean factory to configure a pre-existing object given the name of a bean definition containing the configuration to be applied. The spring-aspects.jar contains an annotation-driven aspect that exploits this capability to allow dependency-injection of any object. The support is intended to be used for objects created outside of the control of any container. Domain objects often fall into this category: they may be created programmatically using the new operator, or by an ORM tool as a result of a database query.

The DependencyInjectionInterceptorFactoryBean in the org.springframework.orm.hibernate.support package can be used to have Spring create and configure prototype domain objects for Hibernate (using either autowiring or named prototype bean definitions). The interceptor does not of course support configuration of objects that you create yourself programmatically rather than retrieve from the database. Similar techniques may be possible with other frameworks. As ever, be pragramatic and choose the simplest thing that meets your requirements. Please note that the aforementioned class does not ship with the Spring distribution. If you want to make use of it, you will have to download it from the Spring CVS repository and compile it yourself; the file itself can be found in the 'sandbox' directory of the Spring CVS repository tree.

The @Configurable annotation marks a class as eligible for Spring-driven configuration. In the simplest case it can be used just as a marker annotation:

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class Account {
   ...
}

When used simply as a marker interface in this way, Spring will configure new instances of the annotated type (Account in this case) using a prototypical bean definition with the same name as the fully-qualified type name (com.xyz.myapp.domain.Account). Since the default name for a bean is the fully-qualified name of its type, a convenient way to declare the prototype definition is simply to omit the id attribute:

<bean class="com.xyz.myapp.domain.Account" scope="prototype">
  <property name="fundsTransferService" ref="fundsTransferService"/>
  ...
</bean>

If you want to explicitly specify the name of the prototype bean definition to use, you can do so directly in the annotation:

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable("account")
public class Account {
   ...
}

Spring will now look for a bean definition named "account" and use that as a prototypical definition to configure new Account instances.

You can also use autowiring to avoid having to specify a prototypical bean definition at all. To have Spring apply autowiring use the autowire property of the @Configurable annotation: specify either @Configurable(autowire=Autowire.BY_TYPE) or @Configurable(autowire=Autowire.BY_NAME for autowiring by type or by name respectively.

Finally you can enable Spring dependency checking for the object references in the newly created and configured object by using the dependencyCheck attribute (for example: @Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true) ). If this attribute is set to true, then Spring will validate after configuration that all properties (that are not primitives or collections) have been set.

Merely using the annotation on its own does nothing of course. It's the AnnotationBeanConfigurerAspect in spring-aspects.jar that acts on the presence of the annotation. In essence the aspect says "after returning from the initialization of a new object of a type with the @Configurable annotation, configure the newly created object using Spring in accordance with the properties of the annotation". For this to work the annotated types must be woven with the AspectJ weaver - you can either use a build-time ant or maven task to do this (see for example the AspectJ Development Environment Guide) or load-time weaving (see Section 6.8.4, “Using AspectJ Load-time weaving (LTW) with Spring applications”).

The AnnotationBeanConfigurerAspect itself needs configuring by Spring (in order to obtain a reference to the bean factory that is to be used to configure new objects). The Spring AOP namespace defines a convenient tag for doing this. Simply include the following in your application context configuration:

<aop:spring-configured/>

If you are using the DTD instead of schema, the equivalent definition is:

<bean 
  class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"
  factory-method="aspectOf"/>

Instances of @Configurable objects created before the aspect has been configured will result in a warning being issued to the log and no configuration of the object taking place. An example might be a bean in the Spring configuration that creates domain objects when it is initialized by Spring. In this case you can use the "depends-on" bean attribute to manually specify that the bean depends on the configuration aspect.

<bean id="myService"
  class="com.xzy.myapp.service.MyService"
  depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
  ...
</bean>

6.8.1.1. Unit testing @Configurable objects

One of the goals of the @Configurable support is to enable independent unit testing of domain objects without the difficulties associated with hard-coded lookups. If @Configurable types have not been woven by AspectJ then the annotation has no affect during unit testing, and you can simply set mock or stub property references in the object under test and proceed as normal. If @Configurable types have been woven by AspectJ then you can still unit test outside of the container as normal, but you will see a warning message each time that you construct an @Configurable object indicating that it has not been configured by Spring.

6.8.1.2. Working with multiple application contexts

The AnnotationBeanConfigurerAspect used to implement the @Configurable support is an AspectJ singleton aspect. The scope of a singleton aspect is the same as the scope of static members, i.e. there is one aspect instance per classloader that defines the type. This means that if you define multiple application contexts within the same classloader hierarchy you need to consider where to define the <aop:spring-configured/> bean and where to place spring-aspects.jar on the classpath.

Consider a typical Spring web-app configuration with a shared parent application context defining common business services and everything needed to support them, and one child application context per servlet containing definitions particular to that servlet. All of these contexts will co-exist within the same classloader hierarchy, and so the AnnotationBeanConfigurerAspect can only hold a reference to one of them. In this case we recommend defining the <aop:spring-configured/> bean in the shared (parent) application context: this defines the services that you are likely to want to inject into domain objects. A consequence is that you cannot configure domain objects with references to beans defined in the child (servlet-specific) contexts using the @Configurable mechanism (probably not something you want to do anyway!).

When deploying multiple web-apps within the same container, ensure that each web-application loads the types in spring-aspects.jar using its own classloader (for example, by placing spring-aspects.jar in WEB-INF/lib). If spring-aspects.jar is only added to the container wide classpath (and hence loaded by the shared parent classloader), all web applications will share the same aspect instance which is probably not what you want.

6.8.2. Other Spring aspects for AspectJ

In addition to the @Configurable support, spring-aspects.jar contains an AspectJ aspect that can be used to drive Spring's transaction management for types and methods annotated with the @Transactional annotation. This is primarily intended for users who want to use Spring's transaction support outside of the Spring container.

The aspect that interprets @Transactional annotations is the AnnotationTransactionAspect. When using this aspect, you must annotate the implementation class (and/or methods within that class), not the interface (if any) that the class implements. AspectJ follows Java's rule that annotations on interfaces are not inherited.

A @Transactional annotation on a class specifies the default transaction semantics for the execution of any public operation in the class.

A @Transactional annotation on a method within the class overrides the default transaction semantics given by the class annotation (if present). Methods with public, protected, and default visibility may all be annotated. Annotating protected and default visibility methods directly is the only way to get transaction demarcation for the execution of such operations.

For AspectJ programmers that want to use the Spring configuration and transaction management support but don't want to (or can't) use annotations, spring-aspects.jar also contains abstract aspects you can extend to provide your own pointcut definitions. See the Javadocs for AbstractBeanConfigurerAspect and AbstractTransactionAspect for more information. As an example, the following excerpt shows how you could write an aspect to configure all instances of objects defined in the domain model using prototypical bean definitions that match the fully-qualified class names:

public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {

  public DomainObjectConfiguration() {
    setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
  }

  // the creation of a new bean (any object in the domain model)
  protected pointcut beanCreation(Object beanInstance) :
    initialization(new(..)) &&
    SystemArchitecture.inDomainModel() && 
    this(beanInstance);
		   		   
}

6.8.3. Configuring AspectJ aspects using Spring IoC

When using AspectJ aspects with Spring applications, it's natural to want to configure such aspects using Spring. The AspectJ runtime itself is responsible for aspect creation, and the means of configuring the AspectJ created aspects via Spring depends on the AspectJ instantiation model (per-clause) used by the aspect.

The majority of AspectJ aspects are singleton aspects. Configuration of these aspects is very easy, simply create a bean definition referencing the aspect type as normal, and include the bean attribute 'factory-method="aspectOf"'. This ensures that Spring obtains the aspect instance by asking AspectJ for it rather than trying to create an instance itself. For example:

<bean id="profiler" class="com.xyz.profiler.Profiler"
      factory-method="aspectOf">
  <property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

For non-singleton aspects the easiest way to configure them is to create prototypical bean definitions and annotate use the @Configurable support from spring-aspects.jar to configure the aspect instances once they have bean created by the AspectJ runtime.

If you have some @AspectJ aspects that you want to weave with AspectJ (for example, using load-time weaving for domain model types) and other @AspectJ aspects that you want to use with Spring AOP, and these aspects are all configured using Spring then you'll need to tell the Spring AOP @AspectJ autoproxying support which subset of the @AspectJ aspects defined in the configuration should be used for autoproxying. You can do this by using one or more <include/> elements inside the <aop:aspectj-autoproxy/> declaration. Each include element specifies a name pattern, and only beans with names matched by at least one of the patterns will be used for Spring AOP autoproxy configuration:

<aop:aspectj-autoproxy>
  <include name="thisBean"/>
  <include name="thatBean"/>
</aop:aspectj-autoproxy>

6.8.4. Using AspectJ Load-time weaving (LTW) with Spring applications

Load-time weaving (or LTW) refers to the process of weaving AspectJ aspects with an application's class files as they are loaded into the VM. For full details on configuring load-time weaving with AspectJ, see the LTW section of the AspectJ Development Environment Guide . We will focus here on the essentials of configuring load-time weaving for Spring applications running on Java 5.

Load-time weaving is controlled by defining a file 'aop.xml' in the META-INF directory. AspectJ automatically looks for all 'META-INF/aop.xml' files visible on the classpath and configures itself based on the aggregation of their content.

A basic META-INF/aop.xml for your application should look like this:

<!DOCTYPE aspectj PUBLIC    
  "-//AspectJ//DTD//EN"    "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    

<aspectj>    
   <weaver>
	 <include within="com.xyz.myapp..*"/>
   </weaver>
</aspectj>

The <include/>' element tells AspectJ what set of types should be included in the weaving process. Use the package prefix for your application followed by "..*" (meaning '... and any type defined in a subpackage of this') as a good default. Using the include element is important as otherwise AspectJ will look at every type loaded in support of your application (including all the Spring library classes and many more besides). Normally you don't want to weave these types and don't want to pay the overhead of AspectJ attempting to match against them.

To get informational messages in your log file regarding the activity of the load-time weaver, add the following options to the weaver element:

<!DOCTYPE aspectj PUBLIC    
  "-//AspectJ//DTD//EN"    "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    

<aspectj>    
   <weaver 
	 options="-showWeaveInfo
              -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
	 <include within="com.xyz.myapp..*"/>
   </weaver>
</aspectj>

Finally, to control exactly which aspects are used, you can use the aspects element. By default all defined aspects are used for weaving (spring-aspects.jar contains a META-INF/aop.xml file that defines the configuration and transaction aspects). If you were using spring-aspects.jar, but only want the configuration support and not the transaction support you could specify this as follows:

<!DOCTYPE aspectj PUBLIC    
  "-//AspectJ//DTD//EN"    "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    

<aspectj>
  <aspects>
    <include within="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"/>
  </aspects>    
  <weaver 
    options="-showWeaveInfo -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
    <include within="com.xyz.myapp..*"/>
  </weaver>
</aspectj>

On the Java 5 platform, load-time weaving is enabled by specifying the following VM argument when launching the Java virtual machine:

-javaagent:<path-to-ajlibs>/aspectjweaver.jar

6.9. Further Resources

More information on AspectJ can be found at the AspectJ home page.

The book Eclipse AspectJ by Adrian Colyer et. al. (Addison-Wesley, 2005) provides a comprehensive introduction and reference for the AspectJ language.

The excellent AspectJ in Action by Ramnivas Laddad (Manning, 2003) comes highly recommended as an introduction to AOP; the focus of the book is on AspectJ, but a lot of general AOP themes are explored in some depth.