ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring AOP에 대한 개념
    Spring Framework 2023. 10. 5. 13:16

    Spring AOP

    • 관점 지향 프로그래밍
    • 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점을 나눠서 그 관점들을 모듈화하는 것

    • 횡단(가로) 관심사를 분리한다. (cross-cutting)
      • 다른 로직과 분리할 수 없는 어플리케이션 로직을 분리한다.
      • 부가기능을 따로 관리한다.
      • 공통적으로 사용하는 기능들을 분리하여 관리할 수 있는 것을 말합니다.
    • 프록시 패턴 기반의 AOP 구현체
      • 프록시 패턴 : 어떤 객체를 사용하고자 할 때, 객체를 직접적으로 참조하는 것이 아닌, 해당 객체를 대리(Proxy)하는 객체를 통해 대상 객체에 접근하는 방식
      • 접근 제어 및 부가기능을 추가하기 위해
    • 스프링 bean에만 AOP적용 가능
    • 중복 코드 제거, 유지보수 용이, OOP의 문제점 해결
    • 핵심 로직과 부가 기능의 명확한 분리로, 핵심 로직은 자신의 목적 외에 사항들에는 신경쓰지 않는다.
    • Aspect로 모듈화하고 핵심 비즈니스 로직에서 분리해 재사용한다.

     

    AOP가 프록시 방식을 사용하는 이유

    1. 프록시 객체를 생성해 런타임 시점에 삽입이 가능
      • AOP를 적용하는 대상 클래스의 바이트코드를 수정하지 않고, 부가적인 기능을 주입할 수 있도록 한다.
      • 애플리케이션을 실행 중에 동적으로 관심사를 적용할 수 있다.
    2. 코드 재사용과 모듈화
      • 공통적으로 필요한 기능인 로깅, 보안체크, 트랜잭션 관리 등을 하나의 공통 모듈로 분리하여 관리할 수 있다.
    3. 관심사 분리
      •  코드의 응집도 증가, 결합도 감소로 인해 코드의 이해와 관리가 쉬워진다.
    4. 런타임 선택 가능
      • 필요한 관심사를 선택적으로 적용할 수 있다.

     

    주요 용어

    • Aspect
      • Advice + PointCut
      • 흩어진 관심사를 모듈화 한 것
      • 부가기능을 모듈화 함 (AOP의 기본 모듈)
    •  Target
      • Advice가 부가기능(Aspect)을 부여할 대상
      • Advice가 적용될 비즈니스 로직
      • PointCut으로 결정
      • 클래스 혹은 메소드
    • Advice
      • Target에게 제공할 부가기능을 담은 모듈
      • 실질적으로 어떤 일을 어느 시점에 주입할 지에 대한 것
    • Join Point
      • Advice가 적용될 위치나 끼어들 수 있는 위치
      • 메서드 진입 지점, 생성자 호출 시점, 필드에서 값 추출 등의 다양한 시점에 적용 가능
    • Pointcut
      • Join Point의 상세한 스펙을 정의한 것으로 JoinPoint를 선별하는 작업을 한다.
      • "어떤 메소드의 어느 시점에 호출" 하는 것처럼 구체적으로 Advice가 실행되는 지점을 정할 수 있음
      • Target을 지정하는 정규표현식
      • Join Point의 실행 지점과 동일
    • Advisor
      • Pointcut과 Advice를 하나씩 가지고 있는 Object
      • "어떤 메소드에 어느 시점에 어떤 기능을 넣을지"를 정할 수 있음
    • Weaving
      • Join Point에 Advice를 적용하는 방법
    • AOP proxy
      • AOP 기능을 구현하기 위해 만든 프록시 객체
      • JDK 동적 프록시 혹은 CGLIB 프록시 (기본 값은 CGLIB 프록시)

     

    AOP 적용 방식

    1. 컴파일 시점
      • 모든 지점에 적용 가능
      • 컴파일러를 통해 .java -> .class로 만드는 시점에 부가 기능 로직을 추가
      • AspectJ가 제공하는 특별한 컴파일러를 사용해야 한다.
    2. 클래스 로딩 시점
      • 모든 지점에 적용 가능
      • .class 파일을 JVM 내부의 클래스 로더에 보관하기 전에 조작하여 부가 기능 로직을 추가하는 방식
      • 특별한 옵션과 클래스 로더 조작기를 지정해야하므로 운영하기 어려움
    3. 런타임 시점
      • 스프링이 사용하는 방식
      • 컴파일이 끝나고 클래스 로더에 이미 올라가 있는 상태에서 자바가 실행된 다음에 동작하는 런타임 방식
      • 프록시를 통해 부가 기능이 적용
      • 프록시는 메서드 오버라이팅 개념으로 동작하기 때문에 메서드에서만 적용된다.
      • 메서드에서만 적용이 되기 때문에 스프링 bean에만 AOP를 적용 가능하다.
      • 위의 2가지 시점과 다르게 특별한 환경에서 돌아가지 않는다.

     

    예시

    • 사전 작업
    <!-- 의존성 추가 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

     

    • @Around("execution(경로)") 예시
    /*
        @Aspect 어노테이션을 붙여 이 클래스가 Aspect를 나타내는 클래스라는 것을 명시
        @Component를 붙여 스프링 빈으로 등록
        
        @Around 어노테이션은 타겟 메서드를 감싸서 특정 Advice를 실행한다는 의미
        execution(* com.saelobi..*.EventService.*(..))가 의미하는 바는 com.saelobi 아래의 패키지 경로의 EventService 객체의 모든 메서드에 이 Aspect를 적용하겠다는 의미
    */
    
    @Component
    @Aspect
    public class PerfAspect {
        @Around("execution(* com.saelobi..*.EventService.*(..))")
            public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
            long begin = System.currentTimeMillis();
            Object retVal = pjp.proceed(); // 메서드 호출 자체를 감쌈
            System.out.println(System.currentTimeMillis() - begin);
            return retVal;
        }
    }

     

    • @Around("@annotation(어노테이션)") 예시
    /*
        @PerLogging 어노테이션이 붙은 메서드만 Aspect가 적용
    */
    @Component
    @Aspect
    public class PerfAspect {
        @Around("@annotation(PerLogging)")
            public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
            long begin = System.currentTimeMillis();
            Object retVal = pjp.proceed(); // 메서드 호출 자체를 감쌈
            System.out.println(System.currentTimeMillis() - begin);
            return retVal;
        }
    }

     

    • 스프링 bean의 모든 메서드에 적용
    /*
        SimpleEventService의 모든 메서드에 해당 Aspect가 추가
    */
    @Component
    @Aspect
    public class PerfAspect {
        @Around("bean(simpleEventService)")
        public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
            long begin = System.currentTimeMillis();
            Object retVal = pjp.proceed(); // 메서드 호출 자체를 감쌈
            System.out.println(System.currentTimeMillis() - begin);
            return retVal;
        }
    }

     

     

    출처)

    https://velog.io/@jinyeong-afk/%EA%B8%B0%EC%88%A0-%EB%A9%B4%EC%A0%91-Spring-Transactional-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%ACfeat.-AOP

    https://velog.io/@backtony/Spring-AOP-%EC%B4%9D%EC%A0%95%EB%A6%AC

    https://engkimbs.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81AOP

Designed by Tistory.