0. AOP란?
개발을 하다보면 공통 로직이 생길 수 밖에 없다. 예를들면 예외처리, 시간 측정 등이 해당된다. 이러한 공통 로직을 줄이고 핵심 로직을 부각시키기 위한 것이 AOP의 특징이다.
1. 포스팅의 목표
AOP구현에 사용되는 도구를 알아보고 적재 적소에 잘 활용할 수 있어지는 것이 이번 포스팅의 목표이다.
2. 사전 용어 정리
- Aspect : 여러 객체에 공통적으로 적용되는 로직
- Joinpoint : 프로그램이 실행 중 발생하는 메서드, 생성자, 필드 값 변경 등을 가르키는 특수한 지점
- Advice : 특정 Joinpoint의 Aspect에 의한 동작. 대상 객체의 Joinpoint에 Weaving되어 동작할 수 있는 코드.
- Weaving : Aspect 클래스에 정의 한 Advice 로직을 타깃에 적용하는 것을 의미. Advice를 비즈니스 로직 코드에 삽입하는 것.
- Pointcut : Joinpoint의 정규 표현식. Joinpoint가 Pointcut에 일치할 때마다 해당 Pointcut에 관련된 Advice로직이 수행됨
3. AOP를 구현하는 2가지 방법
이러한 AOP를 구현하는 방법에는 2가지가 있다.
3-1 Spring AOP.
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
스프링의 AOP를 이용하면 컨트롤러에 대한 예외처리, 시간 측정 등 공통 로직 처리를 쉽게 구현할 수 있다.
아래는 스프링 AOP를 사용하여 예외처리 핸들러를 구현한 것이다.
구현에 앞서 ResponseEntity와 ErrorCode 객체를 만들고, Exception을 상속받아 커스터마이징된 예외를 만들어주었다.
3-2-1 Spring AOP의 Advice를 활용한 예외처리 핸들러 구현
@Slf4j
@RestControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IdAlreadyExistException.class)
public ResponseEntity<BaseResponse> handleIdAlreadyExist() {
log.info(ErrorCode.IdAlreadyExist.getMessage());
return ResponseEntity.badRequest().body(new BaseResponse(ErrorCode.IdAlreadyExist));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<BaseResponse> handleNotValid(MethodArgumentNotValidException exception) {
String validateMsg = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage();
log.info(validateMsg);
return ResponseEntity.badRequest().body(new BaseResponse(4001, validateMsg));
}
}
3-2 Aspectj
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
Aspectj 를 사용하는 것과 Spring AOP를 사용하는 객체에 대한 패키지부터 다르다.
3-2-1 Aspectj 예외처리 핸들러 구현
Aspectj를 사용해서 컨트롤러에 대한 예외처리를 구현한 예제이다.
@Aspect
@Component
public class ExceptionControllerHandler {
@Pointcut("execution(* com.example.demo.*Controller.*(..))")
private void cut() {
}
@Around("cut()")
public Object exceptionAop(ProceedingJoinPoint pjp) {
System.out.println("start");
Object proceed = null;
try {
proceed = pjp.proceed();
} catch (FirstException e) {
System.out.println("f1");
return ResponseEntity.status(400).body("f1 error");
} catch (SecondException e) {
System.out.println("f2");
return ResponseEntity.status(400).body("f2 error");
} catch (Throwable e) {
return ResponseEntity.status(400).body("runtime error");
}
return proceed;
}
위 코드를 자세히 보면, Pointcut을 활용하여 컨트롤러 뿐 아니라, 범위를 자유롭게 지정하여 다른 패키지, 객체에 적용 가능한 것을 볼 수 있다.
이는 곧 유연한 확장성과 범용성을 의미한다.
4. Aspectj와 Spring AOP 비교
- 동작방식
```
컴파일 시점 Weaving : AspectJ 컴파일러가 Aspect 코드와 애플리케이션의 소스 모두 입력받아 Weaving된 class 파일을 생성함
컴파일 후 Weaving : 바이너리 Weaving으로도 알려져 있습니다. 이미 존재하는 class 파일과 jar 파일을 Weaving하기 위해 사용됨
로드 시점 Weaving : 위 바이너리 Weaving과 유사하나 Weaving 시점이 class 파일이 JVM에 로드될때 까지 연기된다는 점이 다름
``` - 동작 방식
```
Spring AOP
- DK 동적 Proxy : Spring AOP에서 선호되는 방식입니다. 언제든지 대상 객체가 한 개의 인터페이스를 구현하면 JDK 동적 프록시를 사용할 수 있음
- CGLIB Proxy : 대상 객체가 인터페이스를 구현하지 않는 경우 CGLIB 프록시를 사용할 수 있음.
Aspectj
클래스들이 Aspect와 함께 바로 컴파일되기 때문에 런타임시에는 아무것도 하지 않음.
``` - 한계
4.성능
- Spring AOP
애플리케이션의 시작시에 생성된 Proxy들을 기반으로한 프레임워크이고, 성능에 악영향을 주는 훨씬 적은 수의 메서드만을 지원함.
- AspectJ
Aspect를 애플리케이션이 실행되기 전에 Weaving 하기 때문에 Spring AOP와는 달리 런타임시 과부하가 없음
- 결론
컴파일 시점 Weaving은 런타임 Weaving에 비해 훨씬 빠름.
AspectJ가 Spring AOP보다 8배에서 35배 가까이 빠름.
-- 추가 보충 자료.
Aspectj는 어떻게 컴파일 시점에 Weaving이 이루어질까?
Aspectj 컴파일러
개발자가 AspectJ 코드를 JVM(Java Virtual Machine)에서 실행할 수 있는 바이트코드로 컴파일할 수 있게 해주는 도구
동작 순서
aspectj 코딩 > aspectj 컴파일러 동작 > 자바 코드에 aspectj 컴파일러가 바이트화한 코드를 삽입함. > JVM에 의해 실행
AspectJ 컴파일러와 바이트코드는 개발자가 모듈식 및 재사용 가능한 방식으로 횡단 문제를 구현하여 대규모 Java 애플리케이션을 보다 쉽게 개발하고 유지 관리할 수 있도록 하는 강력한 도구임.
Pointcut 작성 규칙
https://wpunch2000.tistory.com/22
https://sjh836.tistory.com/157
http://closer27.github.io/backend/2017/08/03/spring-aop/
https://heidish.tistory.com/70
'스프링' 카테고리의 다른 글
[JAVA, Spring ] equals, hashcode (0) | 2023.06.12 |
---|---|
Tomcat과 Netty (0) | 2023.02.28 |
JPA, Hibernate, Spring Data JPA (0) | 2023.01.26 |