문제를 바라보는 관점을 기준으로 프로그래밍을 하는 기법
AOP 구현 방법으로는, 여러 가지가 존재하는데, 일반적으로 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드에 삽입하는 것이다.
비슷한 동작을 하는 인터페이스와 필터가 있는데 이들은 웹에서 사용한다.
AOP는 프로그래밍 기법 중 하나이며,
특정 패키지의 특정 클래스 전체, 특정 클래스의 특정 메서드 전체등 적용 범위를 다양하게 설정하여 공통 기능으로 사용할 수 있다.
공통 기능 == 공통 관심 사항
핵심 로직 == 핵심 관심 사항
핵심 비즈니스 기능과 구분하기 위해 공통 기능을 공통 관심 사항(cross-cutting concern)이라고 표현하며, 핵심 로직을 핵심 관심 사항(core concern)이라고 표현한다.
공통 기능 호출 == callModule()
핵심 기능 호출 == business()
스프링은 자체적으로 프록시 기반의 AOP를 지원하므로,
스프링 AOP는 메서드 호출 Joinpoint만을 지원한다.
스프링은 완전한 AOP 기능을 제공하는 것이 아니라,
엔터프라이즈 어플리케이션을 구현하는 데 필요한 기능만을 지원한다.
스프링은 세 가지 방식으로 AOP를 구현할 수 있도록 한다.
AspectJ 를 사용하기 위해서는 pom.xml에 dependency 설정이 필요하다.
공통 기능을 핵심 로직 '언제'(실행 전/후, 예외 발생 전/후) 적용할 것인가를 정의하고 있다.
✍언제 공통 기능을 삽입할 것인가에 대해 구분할 수 있는 기준점이 필요한데, 파라미터로 받는 ProcedingJoinPoint 타입의 proceed() 메서드가 이러한 기준점 역할을 수행한다.
따라서, 반드시 첫번째 파라미터로 ProceedingJoinPoint 타입을 지정해야 한다.
Around Advice 를 제외한 Advice는 좀 더 자세하게 기능을 구현하기 위해서 사용하며, 이들의 구현 방법으로는 조금씩 다르다.
Before Advice를 구현한 클래스의 리턴타입은 일반적으로 "void" 이다.
리턴 값을 갖더라도, 실제 Advice 적용 과정에서 사용되지 않기 때문이다.
After Advice를 구현한 메서드에서 대상 객체가 리턴한 값을 사용하고 싶다면, returning 속성을 사용하여 리턴 값을 전달 받을 파라미터의 이름을 명시한다.
After Throwing Advice 예외 객체를 사용하고 싶다면,
throwing속성을 사용해 예외 객체를 전달받을 파라미터 이름을 명시한다.
✍리턴 값을 전달 받을 파라미터 이름을 명시하여 어떤 값이 리턴 되는지, 어떤 예외가 발생하는지에 따라 다른 Aspect를 적용하기 위해 파라미터 이름을 명시한다.
aop 네임스페이스의 XML 스키마를 지정했다면, 다음과 같이 <aop:config> 태그를 사용하여 AOP 관련 정보를 설정할 수 있다.
<bean id="profiler" class="com.spring.exercise.aop.Profiler"/>
<aop:config>
<aop:aspect id="traceAspect" ref="profiler">
<aop:pointcut id="publicMethod"
expression="execution(public * com.spring.exercise..*(..))"/>
<aop:around pointcut-ref="publicMethod" method="trace"/>
</aop:aspect>
</aop:config>
<bean id="memberService"
class="com.spring.exercise.member.MemberServiceImpl" />
<aop:aspect> 태그의 ref 속성은 Aspect의 공통 기능을 제공할 빈을 설정할 때 사용된다. 위 코드는 Aspect 기능을 제공할 빈으로 "profiler" 빈을 지정하였다.
<aop:aspectj-autoproxy/> 태그를 등록해주지 않으면, @Aspect 관련 어노테이션은 사용할 수 없다.
메서드에 @Pointcut 을 적용할 수도 있는데, 이렇게 적용된 메서드를 @Around 속성에서 호출하여 간단하게 설정할 수 있고, 여러 Advice에서 사용할 수도 있다.
@Pointcut 어노테이션이 적용된 메서드를 참조하려면, 아래와 같이 입력하면 된다.
Joinpoint 는 Pointcut을 포괄하는 개념
따라서, Pointcut은 세세하게 지정하기 위해서 사용하는것이다.
Around Advice는 핵심 기능 전/후가 지정되지 않았기 때문에, JoinPoint를 기준으로 전/후를 구분하므로, JoinPoint 타입을 반드시 첫번째 매개 변수로 받아 사용해야 한다.
Around Advice 를 제외한 나머지 Advice 타입을 구현한 메서드는 JoinPoint 객체를 선택적으로 사용할 수 있다.
ProceedingJoinPoint의 proceed() 메서드를 호출하면 프록시 대상 객체의 실제 메서드를 호출하게 된다. 실제 메서드를 호출하기 때문에, 실제 메서드 전/후를 구분할 수 있게 되는것이다.
✍스프링 AOP는 인터페이스를 이용해서 프록시 객체를 생성되어, 인터페이스 구현 클래스를 instanceof 연산자로 검사하면 false를 리턴한다. 인터페이스 기준이 아닌 클래스 기준으로 프록시가 생성되게 하려면,
<aop:config proxy-class="true"> 태그를 등록한다. 등록한 뒤 구현 클래스를 instanceof 연산자로 검사하면 올바르게 true를 리턴한다.
execution(수식어? 리턴타입 클래스이름?메서드이름(파라미터)
스프링 AOP는 public 메서드에만 적용 가능하므로 '수식어' 부분은 생략이 가능하다.
각 패턴은 '*'을 이용하여 모든 값을 표현할 수 있고, '..'을 이용하여 0개 이상이라는 의미를 표현할 수 있다.
Advice 적용 순서를 명시적으로 지정하려면, @Order 어노테이션을 적용하거나, Ordered 인터페이스를 구현한다.
@Aspect
public class Exercise implements Ordered {
@Override
public int getOrder() {
return 2;
}
@Order 어노테이션을 사용할 경우 값으로 적용 순서를 지정한다.
@Order(3)
실행 전(Before)는 순차대로 적용되지만, 실행 후(After)는 역순으로 적용된다.
(출처: 웹 개발자를 위한 Spring 4.0 프로그래밍, 저자 : 최범균)