ABOUT ME

프로그래밍 일기

Today
Yesterday
Total
  • AOP
    Spring/스프링가링가링 2022. 11. 9. 02:31

    [Spring] 스프링 AOP (Spring AOP) 총정리 : 개념, 프록시 기반 AOP, @AOP (tistory.com)

    AOP : Aspect Oriented Programming

    - 관점 지향 프로그래밍

    - 취지: 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하는 것

     

    • Aspect : 흩어진 관심사를 모듈화한 것

    • Target : Aspect를 적용하는 곳 (클래스, 메서드 등)

    Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체

    JointPoint : Advice가 적용될 위치. 메서드 진입 지점, 생성자 호출 시점, 필드에서 꺼내올 때 등 다양한 시점에 적용 가능

    PointCut : JointPoint의 상세한 스펙을 정의한 것. Adivce가 실행될 지점을 더 구체적으로 알 수 있음

     

    스프링 AOP 특징

     프록시 패턴 기반의 AOP 구현체, 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위함

     스프링 빈에만 AOP 적용 가능

     모든 AOP 기능을 제공하는 것이 아닌 스프링 IoC와 연동하여 중복 코드 등의 문제에 대한 해결책을 지원하는 것이 목적

     

    build.gradle 파일에 AOP 사용을 위한 의존성 추가

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
        implementation 'org.springframework.boot:spring-boot-starter-aop'  //AOP 의존성 추가
    }

     

    package com.example.springggaop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class AOPTmp {
    
        @Around("execution(* com.example..*.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 : 타겟 메서드를 감싸서 특정 Advice를 실행한다는 의미

    execution(* com.example..*.EventService.*(..))

    : com.example 아래의 패키지 경로의 EventService 객체의 모든 메서드에 해당 Aspect를 적용

     

    package com.example.springggaop.service;
    
    public interface EventService {
    
        void createEvent();
    
        void publishEvent();
    
        void deleteEvent();
    
    }
    package com.example.springggaop.service;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class EventServiceConfig implements EventService{
        
        @Override
        public void createEvent() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Created an event");
        }
    
        @Override
        public void publishEvent() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Published an event");
        }
    
        @Override
        public void deleteEvent() {
            System.out.println("Delete an event");
        }
    }
    package com.example.springggaop.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Service;
    
    @Service
    public class AppRunner implements ApplicationRunner {
    
        @Autowired
        EventService eventService;
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            eventService.createEvent();
            eventService.publishEvent();
            eventService.deleteEvent();
        }
    }

    실행결과

     

    package com.example.springggaop;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.CLASS)
    public @interface PerLogging {
    }

     

    @Component
    @Aspect
    public class AOPTmp {
    
        @Around("@annotation(PerLogging)")

    위와 같이 특정 어노테이션이 붙은 포인트에서만 Aspect를 실행할 수 있는 기능도 있음

     

    createEvent() 와 deleteEvent() 에만 @PerLogging을 붙여주면, 

    실행결과

    위와 같이 두 메소드에서만 Aspect가 실행됨

     

    @Around("bean(EventServiceTmp)")

    위와 같이 하면 스프링 빈의 모든 메서드에 적용된다.

     

     

    @Around말고도 타겟 메서드의 Aspect 실행 시점을 지정할 수 있는 어노테이션들이 많음

     

    • @Before : 메소드 실행 전
    • @After : 메소드 실행 후
    • @AfterReturning : 메소드 정상 실행 후
    • @AfterThrowing : 메소드 예외 발생 후

     


    마법의 코드, 어드바이스 | Moon`s Development Blog (gmoon92.github.io)

     

    Advice

    • 타깃 오브젝트에 적용하는 부가기능을 담은 오브젝트
    • Advisor: PointCut + Advice

    📎 어드바이스의 동작

    • Spring을 포함한 대부분의 AOP 프레임워크는 사용자의 요청을 인터셉터하고, 지정된 어드바이스를 모델링함

    • => 조인 포인트 주변에 지정된 어드바이스 코드가 결합된 상태가 됨
      • 이 상태가 체인같이 보여서 어드바이스 체인이라고 하는 것
    • 실제 런타임 시, 어드바이스 체인이 유지된 상태로 결합한 코드가 실행됨
      • => 어드바이스를 구현할 때, 어느 조인 포인트와 결합할지 명시해주어야 함

     

    📎 어드바이스의 종류

    • Before
      • 비즈니스 메소드 실행 전 동작
    • After Running
      • 비즈니스 메소드가 성공적으로 리턴되면 동작
      • 메소드 내부에 리턴값이 존재하는 경우만!
    • After Throwing
      • 비즈니스 메소드 실행 중, 예외가 발생하면 동작
    • After
      • 비즈니스 메소드 실행 중, 예외가 발생하면 무조건 동작
    • Around
      • 메소드 호출 자체를 가로채서 비즈니스 메소드 실행 전과 후에 모두 처리할 로직을 삽입 가능

     

     

    03. Aspect 작성 - 2 (tistory.com)

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    'Spring > 스프링가링가링' 카테고리의 다른 글

    JPA 지연로딩 사용시 N+1 문제  (0) 2022.10.06
Designed by Tistory.