AOP
AOP란?
AOP = Aspect Oriented Programing
프로그래밍에는 핵심 관심 사항(core concern)과 공통 관심 사항(cross-cutting concern)이 존재한다. 기존 OOP에서는 공통 관심 사항을 여러 모듈에서 적용하는데 있어 중복된 코드를 양상하는 한계가 존재한다. 이를 해결하기 위해 AOP가 등장했다. AOP는 문제를 해결하기 위한 핵심 관심 사항과 전체에 적용되는 공통 관심 사항을 기준으로 프로그래밍함으로써 공통 모듈을 손쉽게 적용할 수 있게 했다.
AOP는 application에서 관심사(기능)을 분리했다. 즉, 핵심 기능에서 부가적인 기능을 분리했다. 분리한 부가 기능을 어스펙트(Aspect)라는 독특한 모듈 형태로 만들어 설계하고 개발하는 방법이 AOP이다. 즉, 부가기능을 어스펙트로 정의하여 핵심기능에서 부가기능을 분리해 핵심기능 설계, 구현에서 객체지향적 가치를 지킬 수 있도록 도와주는 것이다.
AOP 적용 예시
- 간단한 메소드의 성능 검사
개발 도중, 특히 DB에 대량을 데이터를 넣고 빼는 등의 작업 시간 측정은 중요하다. 매번 해당 메소드의 처음과 끝에 System.currentTimeMillis(); 메소드를 사용하거나 스프링의 StopWatch 코드를 사용하기엔 번거로우니 이런 경우 해당 작업을 하는 코드를 밖에 설정하는 편이 편리하다.
- 트랜잭션 처리
트랜잭션의 경우, 비즈니스 로직의 전후에 설정되지만 매번 사용하는 try-catch 부분의 코드는 번거롭고 소스를 복잡하게 만든다.
- 예외 반환
구조가 좋지 않은 예외들이 발생했을 때, 그것을 잡아서 잘 정의 되어있는 예외 계층 구조(ex.스프링의 DataAccessException)로 변환해 다시 던질 때 편리하다.
AOP 구조
- 핵심 관심 사항 : BankingService, AccountService, CustomerService
- 공통 관심 사항 : Security, Transaction, ...
Sprong AOP
Spring AOP 용어
- Target
핵심기능을 담고 있는 모듈. target은 부가기능을 부여할 대상이 된다.
- Advice
어느 시점(ex. method의 수행 전/후, 예외 발생 후 등)에 어떤 공통 관심 기능(Aspect)을 적용할지 정의한 것.
target에 제공할 부가기능을 담고 있는 모듈이다.
- JoinPoint
Aspect가 적용될 수 있는 지점(method, field).
target 객체가 구현한 인터페이스의 모든 method는 JoinPoint이다.
- Pointcut
공통 관심 사항이 적용될 JoinPoint.
Advice를 적용할 target의 method를 선별하는 정규 표현식이다. Pointcut 표현식은 execution으로 시작하고 method의 Signature를 비교하는 방법을 주로 이용한다.
- Aspect
Aspect = Advice + Pointcut
여러 객체에서 공통으로 적용되는 공통 관심 사항(transaction, logging, security, ...).
AOP의 기본 모듈이며 싱글톤 형태의 객체로 존재한다.
- Advisor
Advisor = Advice + pointcut
Spring AOP에서만 사용되는 특별한 용어이다.
- Weaving
어떤 Advice를 어떤 Pointcut(핵심사항)에 적용시킬 것인지에 대한 설정(Advisor).
Pointcut에 의해 결정된 타겟의 JoinPoint에 부가기능(Advice)을 삽입하는 과정을 뜻한다. Weaving은 AOP의 핵심기능(Target)의 코드에 영향을 주지 않으면서 필요한 부가기능(Advice)을 추가할 수 있도록 해주는 핵심적인 처리 과정이다.
Pointcut 표현식
AOP와 기본 애플리케이션 비교
Spring AOP 특징
- Spring은 프록시(Proxy) 기반 AOP를 지원함
- Spring은 Target 객체에 대한 Proxy를 만들어 제공함
- Target을 감싸는 Proxy는 실행시간(Runtime)에 생성
- Proxy는 Advice를 Target 객체에 적용하면서 생성되는 객체
- 프록시(Proxy)가 호출을 가로챔 (Intercept)
- Proxy는 Target 객체에 대한 호출을 가로챈 다음 Advice의 부가기능 로직을 수행하고 난 후에 Target의 핵심 기능 로직을 호출함(전처리 Advice)
- 또는, Target의 핵심 기능 로직 method를 호출한 후에 부가기능(Advice)를 수행하는 경우도 있음(후처리 Advice)
- Spring AOP는 method JoinPoint만 지원함
- Spring은 동적 Proxy를 기반으로 AOP를 구현하므로 method JoinPoint만 지원함
- 즉, 핵심기능(Target)의 method가 호출되는 런타임 시점에만 부가기능(Advice)를 적용할 수 있음
- 반면 AspectJ 같은 고급 AOP framework를 사용하면 객체의 생성, 필드값의 조화와 조작, static method 호출 및 초기화 등의 다양한 작업에 부가기능을 적용할 수 있음
Spring AOP 구현
Spring AOP의 구현 방법은 크게 세 가지로 나눠진다.
- POJO Class를 이용한 AOP 구현
- Spring API를 이용한 AOP 구현
- Annotation을 이용한 AOP 구현
POJO 기반 AOP 구현 방법
XML Schema 확장 기법을 통해 설정 파일을 작성하고 POJO 기반 Advice Class를 작성한다.
XML Schema 를 사용한 설정 파일
- aop namespace와 XML Schema 추가
- aop namespace를 이용한 설정
- AOP 설정 태그
- Advice 설정 태그
- <aop:aspect>
- 한 개의 Aspect(공통 관심 기능)을 설정
- ref 속성을 통해 공통 기능을 가지고 있는 bean을 연결
- id는 이 태그의 식별자로 지정
- 자식 태그로 <aop:pointcut> advice 관련 태그가 올 수 있음
- <aop:pointcut>
- Pointcut(공통 기능이 적용될 곳)을 지정하는 태그
- <aop:config>나 <aop:aspect>의 자식 태그
config는 전역적으로 사용하고, aspect는 내부에서 사용한다.
- AspectJ 표현식을 통해 pointcut 지정
- id : 식별자로 advice 태그 내에서 사용됨
- expression : pointcut 지정
<aop:pointcut id="logmethod" expression="execution(public com.test.aop...*(..))"/>
POJO 기반 Advice Class 작성
Advice 작성
- 설정 파일의 advice 관련 태그에 맞게 작성한다.
- <bean>으로 등록하며 <aop:aspect>의 ref 속성으로 참조한다.
- 공통 기능 메소드 : advice 관련 태그들의 method 속성의 값이 method의 이름이 된다.
Advice 정의 관련 태그
- pointcut : 직접 pointcut을 설정. 호출할 method의 패턴 지정.
- pointcut-ref : <aop:pointcut> 태그의 id명을 넣어 pointcut 지정.
- method : Aspect bean에서 호출할 method명 지정.
Advice Class
- POJO 기반의 Class로 작성.
- class 명이나 method 명에 대한 제한은 없음.
- method 구문은 호출되는 시점에 따라 달라질 수 있음.
- method의 이름은 advice 태그(<aop:before>)에서 method 속성의 값이 method 명이 됨.
Advice 종류
Before Advice
- 대상 객체의 메소드가 실행되기 전에 실행됨
- return type : 리턴 값을 갖더라도 실제 Advice의 적용과정에서 사용되지 않기 때문에 void를 쓴다.
- argument : 없거나 대상객체 및 호출되는 메소드에 대한 정보 또는 파라미터에 대한 정보가 필요하다면 JoinPoint 객체를 전달한다.
Before Advice 실행 순서
- 빈 객체를 사용하는 코드에서 스프링이 생성한 AOP 프록시의 메소드를 호출
- AOP 프록시는 <aop:before>에서 지정한 메소드를 호출
- AOP 프록시는 Aspect 기능 실행 후 실제 빈 객체의 메소드를 호출
After Returning Advice
- 대상 객체의 메소드 실행이 정상적으로 끝난 뒤에 실행됨
- return type : void
- argument : 없거나 org.aspectj.lang.JoinPoint 객체를 받는다. JoinPoint는 항상 첫 argument로 사용된다. 대상 method에서 반환되는 특정 객체 타입의 값을 method로 받을 수 있다.
After Returning Advice 실행 순서
- 빈 객체를 사용하는 코드에서 스프링이 생성한 AOP 프록시의 메소드를 호출
- AOP 프록시는 실제 빈 객체의 메소드를 호출(정상 실행)
- AOP 프록시는 <aop:after-returning>에서 지정한 메소드를 호출
After Throwing Advice
- 대상 객체의 메소드 실행 중 예외가 발생한 경우 실행됨
- return type : void
- argument : 없거나 JoinPoint 객체를 받는다. JoinPoint는 항상 첫 argument로 사용된다. 대상 method에서 전달되는 예외 객체를 argument로 받을 수 있다.
After Throwing Advice 실행 순서
- 빈 객체를 사용하는 코드에서 스프링이 생성한 AOP 프록시의 메소드를 호출
- AOP 프록시는 실제 빈 객체의 메소드를 호출(exception 발생)
- AOP 프록시는 <aop:after-throwing>에서 지정한 메소드를 호출
After Advice
- 대상 객체의 메소드가 정상적으로 실행되었는지 exception을 발생 시켰는지 여부와 관계 없이 메소드 실행 후 공통 기능 적용
- return type : void
- argument : 없거나 JoinPoint 객체를 받는다. JoinPoint는 항상 첫 argument로 사용된다.
After Advice 실행 순서
- 빈 객체를 사용하는 코드에서 스프링이 생성한 AOP 프록시의 메소드를 호출
- AOP 프록시는 실제 빈 객체의 메소드를 호출(정상 실행, exception 발생 : java의 finally와 같음)
- AOP 프록시는 <aop:after>에서 지정한 메소드를 호출
Around Advice
- 위의 네 가지 Advice를 모두 구현할 수 있는 Advice
- return type : Object
- argument : org.aspectj.lang.ProceedingJoinPoint를 반드시 첫 argument로 지정한다.
Around Advice 실행 순서
- 빈 객체를 사용하는 코드에서 스프링이 생성한 AOP 프록시의 메소드를 호출
- AOP 프록시는 <aop:around>에서 지정한 메소드를 호출
- AOP 프록시는 실제 빈 객체의 메소드를 호출
- AOP 프록시는 <aop:around>에서 지정한 메소드를 호출
JoinPoint Class 구성 요소
- 대상 객체에 대한 정보를 가지고 있는 객체로 Spring Container로부터 받는다.
- org.aspectj.lang 패키지에 있다.
- 반드시 Aspect method의 첫 argument로 와야 한다.
- 주요 method
@aspect Annotation을 이용한 AOP 구현
- @Aspect Annotation을 이용하여 Aspect Class에 직접 Advice 및 Pointcut 등을 설정.
- 설정 파일에 <aop:aspectj-autoproxy> 추가.
- Aspect Class를 <bean>으로 등록.
- 어노테이션(Annotation)
- @Aspect : Aspect Class 선언
- @Before("pointcut")
- @AfterReturning(pointcut="", returning="")
- @AfterThrowing(pointcut="", throwing="")
- @After("pointcut")
- @Around("pointcut")
- Around를 제외한 나머지 method들은 첫 argument로 JoinPoint를 가질 수 있다.
- Around method는 argument로 ProceedingJoinPoint를 가질 수 있다.