AOP의 핵심 용어 정리
- Target(타겟) : 공통 기능이 부여될 대상을 의미합니다. 클래스 혹은 메소드가 될 수 있습니다.
- Aspect(애스팩트) : 여러 객체에 적용될 공통 기능을 의미합니다. ex) 보안, 로그, DB처리
- Advice(어드바이스) : 공통 기능이 부여될 시점을 의미합니다. ex) 메소드 호출 전, 메소드 실행 이후
- JoinPoint(조인포인트) : Advice가 적용 될 수 있는 지점을 의미하며 스프링의 경우 프록시 방식의 메소드 조인포인트를 지원합니다. 다른 AOP 방식의 경우 필드에도 적용이 가능하다.
- PointCut(포인트컷) : 여러 개의 조인 포인트를 모아 포인트컷이라 부릅니다.
AOP Advice의 종류
<aop:before>
: 메소드가 실행되기 전에 실행<aop:after-returning>
: 메소드가 정상 실행 된 후 실행<aop:after-throwing>
: 메소드 실행 중 Exception 발생 시 실행<aop:after>
: 메소드 실행 중 Exception이 발생하여도 실행<aop:around>
: 메소드 실행 전 1회, 실행 후 1회 및 Exception 발생 시 실행
- 스프링 AOP에서는 주로
around advice
나before
를 사용한다.
스프링에 AOP 구현하기
앞서 살펴본 바와 같이 스프링은 프록시를 이용한 AOP를 제공한다.
이 방법에는 POJO 클래스를 이용한 XML 적용과 어노테이션(@Aspect)으로 두 가지가 있다.
- POJO(Plain Old Java Object) : 말 그대로 해석을 하면 오래된 방식의 간단한 자바 오브젝트라는 말로서 Java EE 등의 중량 프레임워크들을 사용하게 되면서 해당 프레임워크에 종속된 “무거운” 객체를 만들게 된 것에 반발해서 사용되게 된 용어이다. (출처 : 위키피디아)
1. XML을 이용한 스프링 AOP
XML을 이용한 스프링 AOP를 사용할 경우, Aspect를 어디에 언제 적용할지를 설정한다.
이때 Aspect 클래스는 POJO(순수 자바 객체)이다.
적용을 위한 작업 순서
Dependency 설정
pom.xml에 의존 라이브러리(aspectj) 추가
1
2
3
4
5<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>공통 코드(Proxy Code) 작성
Aspect 코드 구현
1
2
3
4
5
6
7
8
9
10
11public Object toLog(ProceedingJoinPoint joinpoint) throws Throwable {
String methodName = joinpoint.getSignature().getName(); // 메소드명
long startTime = System.nanoTime(); // 시작시간
try {
Object obj = joinpoint.proceed(); // 핵심 기능 실행
return obj;
} finally {
long endTime = System.nanoTime(); // 종료시간
System.out.println("[" + methodName + "] is " + (endTime - startTime));
}
}
Aspect 코드 구현 시, 스프링 AOP에서 가장 많이 사용되는 Advice인 Around Advice로 구현할 경우 ProceedingJoinPoint
의 proceed()를 통해 핵심 기능을 실행시킨다.
- config.xml 설정
AOP 설정 파일에 Aspect 빈 등록 및 PointCut 설정
1
2
3
4
5
6
7
8
9<!-- 공통 기능을 제공할 클래스를 빈에 등록처리 -->
<bean id="logAct" class="com.jhdev.asfect.LogAspect" />
<!-- Aspect 설정: Advice를 어떤 Pointcut에 적용할 지 설정 -->
<aop:config>
<aop:aspect ref="logAct">
<aop:around pointcut="execution(* com.jhdev.board.service..*Impl.*(..))" method="toLog"/>
</aop:aspect>
</aop:config>
2. 어노테이션을 이용한 스프링 AOP
어노테이션을 이용한 AOP 설정은 XML을 이용한 방법과 유사하지만 비교적 더 간단하다.
공통 기능 클래스에 어노테이션과 함께 PointCut을 설정한다.
- 어노테이션을 이용하는 방법을 추천한다. 간결하고 생산성이 빠를 수 있다.
적용을 위한 작업 순서
Dependency 설정
pom.xml에 의존 라이브러리(aspectj) 추가
1
2
3
4
5<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>공통 코드(Proxy Code) 작성
Aspect 코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// Aspect Anotation
public class LogAspect {
// pointcut 설정
"within(com.jhdev.board.service.*)") (
private void pointcutMethod() {
}
// pointcut 메소드 설정
"pointcutMethod()") (
public Object toLog(ProceedingJoinPoint joinpoint) throws Throwable {
String methodName = joinpoint.getSignature().getName(); // 메소드명
long startTime = System.nanoTime(); // 시작시간
try {
Object obj = joinpoint.proceed(); // 핵심 기능 실행
return obj;
} finally {
long endTime = System.nanoTime(); // 종료시간
System.out.println("[" + methodName + "] is " + (endTime - startTime));
}
}
// pointcut 직접 설정
"execution(public * com.jhdev.board.service..*(..))") (
public void beforeTest() {
System.out.println("Before Advice");
}
Aspect 코드 구현 시, XML POJO 방식과 대부분 동일하지만 다른 점은 pointcut과 advice 설정을 Aspect 구현 클래스에서 한다.
- config.xml 설정
AOP를 xml에 설정하던 것과 다르게 빈과 autoproxy만을 설정
1 | <!-- 공통 기능을 제공할 클래스를 빈에 등록처리 --> |
AspectJ의 PointCut 표현 방식
AspectJ 사용 시 PointCut을 명시하는 방법에 대해 알아보자
이를 AspectJ 문법이라고 부른다.
AOP는 다양한 핵심 기능에 공통 기능을 적용해야하기 때문에 AspectJ 문법으로 범위를 만들어야 한다.
기본 문법
*
: 모든 클래스 혹은 메소드.
: 현재 패키지..
: 0개 이상
AspectJ PointCut 명시자
execution
: [접근명시자패턴] 리턴타입패턴 이름패턴(파라미터패턴) 형태로 세밀한 명시가 가능within
: 특정 메소드가 아닌 특정 패키지 하위에 모든 메소드을 명시하는 경우
Execution
execution([접근명시자패턴] 리턴타입패턴 이름패턴(파라미터패턴))
execution(publid void set*(..))
리턴 타입이 void이면서 메소드명이 set으로 시작하는 경우
execution(* com.jhdev.board.service.*.*())
해당 패키지 내 클래스 중 파라미터가 없는 메소드
execution(* com.jhdev.board.service..*.*())
해당 패키지와 해당 패키지 하위에 존재하는 패키지 내 파라미터가 없는 메소드
WithIn
within(com.jhdev.board.service.*())
해당 패키지 내 모든 클래스의 메소드
within(com.jhdev.board.service..*())
해당 패키지와 해당 패키지 하위에 존재하는 패키지 내 모든 클래스의 메소드