AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다.
관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화 하겠다는 것이다.
소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 발견할 수 있는 데 이것을 흩어진 관심사 (Crosscutting Concerns)라 부른다.
동작시점 | 설명 |
Before | 메소드 실행 전에 동작 |
After | 메소드 실행 후에 동작 |
After-returning | 메소드가 정삭적으로 실행된 후에 동작 |
After-throwing | 예외가 발생한 후에 동작 |
Around | 메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작 |
Spring AOP는 프록시 패턴이라는 디자인 패턴을 사용해서 AOP 효과를 낸다.
프록시 패턴을 사용하면 어떤 기능을 추가하려 할 때 기존 코드를 변경하지 않고 기능을 추가할 수 있다.
어떤 클래스가 Spring AOP의 대상이라면 그 기존 클래스의 빈이 만들어질 때 Spring AOP가 프록시(기능이 추가된 클래스)를 자동으로 만들고 원본 클래스 대신 프록시를 빈으로 등록한다.
그리고 원본 클래스가 사용되는 지점에서 프록시를 대신 사용한다.
Proxy는 실제 Target의 기능을 수행하면서 기능을 확장하거나 추가하는 실제 객체(Object)를 의미한다.
Proxy Pattern은 Client가 Target에 접근하는 방식을 변경해 주는 역할을 한다.
호출자(클라이언트)에서 타겟을 호출하게 되면 타겟이 아닌 타겟을 감싸고 있는 프록시가 호출되어, 타겟 메서드 실행 전에 선처리, 타겟 메서드 실행 후, 후처리를 실행시키도록 구성되어 있습니다.
Spring AOP에서는 런타임시에 Weaving을 통해서 프록시 객체를 생성하게 됩니다.
프록시 패턴에 대해서는 [Spring/개념] - 프록시 패턴(Proxy Pattern) 포스팅을 참고 바란다.
정적으로 프록시를 만들어서 AOP를 구현하는 것이 아니라 프록시를 조건에 따라 자동으로 만들어주는 Dynamic Proxy를 이용한다. Dynamic Proxy에는 JDK Dynamic Proxy와 CGLIB가 있다.
JDK Dynamic Proxy는 반드시 Interface가 정의되어있어야 하고, Interface에 대한 명세를 기준으로 Proxy를 생성한다.
CGLIB Proxy는 순수 Java JDK 라이브러리를 이용하는 것이 아닌 CGLIB라는 외부 라이브러리를 추가해야만 사용할 수 있다. Interface가 없어도 Proxy를 생성할 수 있다. CGLIB Proxy는 Target Class를 상속받아 생성된다. 그렇기 때문에 개발자는 Proxy를 생성하기 위해 굳이 Interface를 만들어야 하는 수고를 덜 수 있다.
하지만, 상속을 이용하는 만큼 final이나 private과 같이 상속에 대해 Override를 지원하지 않는 경우 Proxy에서 해당 메서드에 대한 Aspect를 적용할 수 없다는 단점이 있다.
Target Object가 interface를 구현하였다면 JDK Proxy가 사용되고 Target Object가 interface를 구현하지 않았다면 CGLIB Proxy가 사용된다.
@Slf4j
@Aspect
@Component
public class AdviceLogging {
@Before("execution(* com.devlog..*.*(..)) "
+ "&& !execution(* com.devlog.common.jwt..*.*(..)) "
)
public void logging(JoinPoint joinPoint) {
log.info("# method: {}", joinPoint.getSignature());
}
@AfterReturning(pointcut = "execution(* com.devlog..*.*(..)) "
+ "&& !bean(*Mapper) "
+ "&& !execution(* com.devlog.common.jwt..*.*(..)) "
)
public void loggingAfterReturning(JoinPoint joinPoint) {
log.info("# method: {}", joinPoint.getSignature());
}
}
[JAVA] Multi-Thread 사용법 (0) | 2022.03.02 |
---|---|
[Spring] IoC 컨테이너 / Bean (0) | 2021.10.14 |
@Autowired 인터페이스 사용이유 (0) | 2021.10.07 |
@Autowired 사용법 (0) | 2021.10.06 |
[Spring] component-scan 사용이유 (0) | 2021.10.05 |
댓글 영역