스프링 AOP에 대해 정리를 해보고자 한다.
1. AOP 개념?
AOP란 Apsects Oriented Programming 으로, 관점 지향 프로그래밍의 약자이다.
어플리케이션은 로직은 크게 핵심기능과 부가 기능으로 나눌 수 있다.
실제 요청을 받아 들이고 처리하는 로직을 핵심기능이라고 하고
핵심 기능을 보조하여 가령 로그를 남겨주는 로직을 부가 기능이라고 한다.
가령 , 주문에 대한 로직이 있다고 가정하자. 그리고 각 컴포넌트에서 로그를 남겨주는 코드를 적용해야한다면
다음과 같이 각 컴포넌트에 로그를 일일이 남겨주는 로직을 핵심기능안에 넣어야 한다.
그런데 로그찍는 방법이 달라졌는데 컴포넌트가 100개가 넘는다면 ..? => 100개가 넘는 로직를 뜯어고쳐야한다.
로그추적과 같은 공통관심사를 핵심기능과 완전히 분리하고자 하는 취지에서 나온것이 AOP 개념이다.
즉 , 이는 기존의 OOP방식으로는 해결이 힘들기 때문에 , OOP를 보조해주는 확장판이라고 할 수 있다.
이를 지원해주는 프레임워크가 있는데 , 바로 AspectJ 이다. (https://www.eclipse.org/aspectj/)
2. AspectJ
AOP를 적용하기 위해서는 AspectJ의 3가지 방법이 있다.
1. 컴파일 시점
.java 파일을 .class로 만들기 전에 AspectJ가 제공하는 컴파일러로 부가 기능 코드를 끼워넣는 방법
2. 클래스로딩 시점
자바를 실행하면 .class 파일을 클래스로더에 보관하는데 , 이 때 AspectJ의 조작기가 작동해서
.class 파일의 바이트코드를 조작하여 부가기능로직을 추가해버린다.
컴파일 시점과 클래스로딩 시점 모두 별도의 조작기나 컴파일러가 필요해서 상당히 복잡해서 잘 쓰지 않는다.
3. 런타임 시점
Spring에서 이 방법을 쓰며 , AspectJ의 문법,애노테이션,인터페이스를 차용해서 사용한다.
말 그대로 , 런 타임 시점에 프록시를 통해서 부가기능을 적용할지 말지 정해논 기준을 참조하여 적용한다.
3. Spring AOP 용어
Advice : 부가기능로직
Pointcut : 어드바이스가 적용되는 기준을 선별 , AspectJ의 문법 이용
Advisor : 하나의 어드바이스 + 하나의 포인트컷
JoinPoint : 어드바이스가 적용될 수 있는 위치나 지점. 스프링은 프록시방식을 이용하므로 메소드의 실행지점
Aspect : 여러 어드바이스 + 포인트컷을 모듈화 한것. @Aspect
AopProxy : AOP기능을 구현하기 위한 프록시 객체. 스프링은 CGLIB방식이 기본이며 권장됨
Target : 어드바이스를 적용 받는 객체
4. 예제
Aspect 클래스를 만들고 @Aspect를 지정해 주고 , @Around (포인트컷) 에서 실행지점을 지정해주면 된다.
@Slf4j
@Aspect
public class Aspect {
//hello.aop.order 패키지와 하위 패키지
@Around("execution(* hello.aop.order..*(..))")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature()); //join point 시그니처
return joinPoint.proceed();
}
}
@Slf4j
@Repository
public class OrderRepository {
public String save(String itemId) {
log.info("[orderRepository] 실행");
//저장 로직
if (itemId.equals("ex")) {
throw new IllegalStateException("예외 발생!");
}
return "ok";
}
}
@Slf4j
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void orderItem(String itemId) {
log.info("[orderService] 실행");
orderRepository.save(itemId);
}
}
// 테스트
@Slf4j
@SpringBootTest
@Import(Aspect.class)
public class AopTest {
@Autowired
OrderService orderService;
@Autowired
OrderRepository orderRepository;
@Test
void aopInfo() {
log.info("isAopProxy, orderService={}",AopUtils.isAopProxy(orderService));
log.info("isAopProxy, orderRepository={}",AopUtils.isAopProxy(orderRepository));
}
@Test
void success() {
orderService.orderItem("itemA");
}
@Test
void exception() {
assertThatThrownBy(() -> orderService.orderItem("ex"))
.isInstanceOf(IllegalStateException.class);
}
}
5. 예시
그 외에 흔히 쓰던 @Transactional 도 AOP 기반으로 동작하고
SLF4J 와 같은 로그 출력 라이브러리
검증할때 사용하는 검증기 중 자바 표준 jsr 말고 hibernate 의 검증기 ( @Validated ) 등
생각보다 잘 모르고 쓰던 것들이 알고보면 AOP기반으로 동작 했던 것들이 많다.
'Backend > Spring' 카테고리의 다른 글
[SpringSecurity] 스프링 시큐리티 구성하기 (0) | 2022.06.07 |
---|---|
[SpringSecurity] 권한 설정 hasRole 오류와 prefix (1) | 2022.06.02 |
[Spring] 폼 유효성(validation) 검사하기 (0) | 2022.05.24 |
Spring 구조와 Component (0) | 2022.05.07 |
부트스트랩 css 적용 안될 때 해결법 (0) | 2022.02.25 |