AOP(Aspect Opriented Programming) 관점 지향 프로그래밍
스프링 어플리케이션은 대부분 특별한 경우를 제외하고는 MVC 웹 어플리케이션에서는 Web Layer, Business Layer, Data Layer로 정의.
- Web Layer : REST API를 제공하며, Client 중심의 로직 적용
- Business Layer : 내부 정책에 따른 logic을 개발하며, 주로 해당 부분을 개발
- Data Layer : 데이터 베이스 및 외부와의 연동을 처리
Annotation | 의미 |
---|---|
@Aspect | AOP를 정의하는 Class에 사용 |
@Pointcut | AOP가 적용될 JoinPoint를 선언 |
@Before | 메서드 실행 전에 적용 |
@After | 메서드 실행 후에 적용 |
@AfterReturning | 메서드가 호출 성공 실행 시 적용 |
@AfterThrowing | 메서드가 호출 실패 예외 발생 시 적용 |
@Around | 메서드 실행 전, 후 또는 예외 발생 시 적용 |
AOP 적용
PointCut 설정
어디에 적용할지 선언해줘야 함
@Aspect
@Component // Spring으로부터 관리되게 해준다.
public class TimerAop {
// Spring에서 관리하는 Bean들에게만 사용할 수 있다.
// 만약 Bean이 아닌 다른 클래스에 사용하고 싶다면 aspectj를 사용해야 한다.
@Pointcut(value = "within(com.example.filter.controller.UserApiController)")
public void timerPointCut() {
}
... other methods
}
상황에 맞는 Annotation 사용
Before
@Before(value = "timerPointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("========== Before ==========");
}
After
@After(value = "timerPointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("========== After ==========");
}
AfterReturning
@AfterReturning(value = "timerPointCut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result/* 결과 값이 여기로 들어온다 */) {
System.out.println("========== After Returning ==========");
System.out.println(MessageFormat.format("========== result : {0} ==========", result));
System.out.println("========== After Returning ==========");
}
AfterThrowing
@AfterThrowing(value = "timerPointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("========== After Throwing ==========");
}
Around
@Around(value = "timerPointCut()") // 어떤 Pointcut 식을 사용할 것인지 지정해줘야 한다.
public void around(ProceedingJoinPoint joinPoint/* 실행전 args 등이 들어감*/) throws Throwable {
System.out.println("메서드 실행 이전");
Arrays.stream(joinPoint.getArgs()).forEach(
it -> {
if(it instanceof UserRequest) {
var tempUser = (UserRequest) it;
String phoneNumber = tempUser.getPhoneNumber().replace("-", "");;
tempUser.setPhoneNumber(phoneNumber);
}
System.out.println(it);
}
);
var stopWatch = new StopWatch();
stopWatch.start();
var newObjs = Arrays.asList(new UserRequest());
// 메서드를 실행시키는 실체를 감싼 부분이다.
joinPoint.proceed(newObjs.toArray()); // 전부 null로 나옴
System.out.println("메서드 실행 이후");
stopWatch.stop();
System.out.println("총 소요 시간 : " + stopWatch.getTotalTimeMillis() + "ms");
}
PointCut 지시자
PCD | 의미 |
---|---|
execution | 메서드 실행 시점 |
within | 특정 경로의 타입을 기준으로 지정 |
this | 특정 타입의 객체를 기준으로 지정 |
target | 특정 타입의 객체를 기준으로 지정 |
args | 특정 인자를 받는 메서드 호출 시점 |
@within | 특정 경로의 Annotation을 기준 |
@target | 특정 Annotation이 붙은 객체를 기준으로 지정 |
@args | 특정 Annotation의 파라미터를 가지는 메서드 호출 시점 |
@annotation | 메서드의 Annotation을 기준 |
bean | 특정 Bean의 메서드 호출 시점 |
PCD 예시
"execution([접근제한자-생략가능][리턴 타입][클래스 이름]메서드 이름)"