개발 무지렁이

[Spring] 관점지향프로그래밍 AOP와 Advice, 프록시 서버(Proxy Server) 본문

Backend/스프링

[Spring] 관점지향프로그래밍 AOP와 Advice, 프록시 서버(Proxy Server)

Gaejirang-e 2023. 5. 1. 17:32

AOP(Aspect Oriented Programming)



공통로직횡단으로 빼서
별도의 모듈(라이브러리 덩어리)로 만들어 놓고, 호출해서 사용

(관점을 핵심기능공통기능으로 분리)
(관심사항을 등록해놓으면, 타겟대상을 찾아서 사전 / 사후처리, Filter와 같은 역할)

WHY?

요구사항 변경에 소스코드 변경을 최소화
🧩범용성 코딩 Style, 🧩직관성

Advice 객체


공통의 기능을 모아둔 객체

🚀. AOP 적용시기

  Around: 사전, 사후
  Before: 사전
  After: 사후 (예외여부 상관없이)
  After-returning: 사후 (정상동작시)
  After-throwing: 사후 (예외발생시)

  ⭐. AspectJ (어노테이션 이용)
  @Around
  @Before
  @After
  @After-returning
  @After-throwing

📌. join point & pointCut
: Advice를 적용할 지점(method) - joinPoint
: 여러개의 join point를 하나의 pointCut으로 묶는다.

⚠️. pointCut: advice 적용시기 결정, proxy server에게 알려줌
⚠️. pointCut + advice = adviser

[TimerAdvice.java]

  public class TimerAdvice {
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          // 사전처리, 타겟대상의 메서드 정보 가져오기
          String methodName = joinPoint.getSignature().getName();
          System.out.println("[ LOG ] " + methodName + "호출 전 사전처리중입니다...");

          Object[] params = joinPoint.getArgs();
          for(Object o : params) {
              System.out.print(o + ", ");
          }
          System.out.println();

          StopWatch sw = new StopWatch();
          sw.start();

          Object obj = joinPoint.proceed(); // 실제 핵심기능을 담당하는 메서드 호출

          // 사후 처리
          sw.stop();
          System.out.println("[ LOG ] " + methodName + " 리턴값 obj: " + obj);
          System.out.println("[ LOG ] " + methodName + " 의 총 실행 ms: " + sw.getTotalTimeMillis() + "ms");

          System.out.println("[ LOG ] " + methodName + " 사후처리 완료되었습니다..\n");

          return obj;
      }
  }

📌. ProceedingJoinPoint joinPoint
: 메서드에 대한 정보가 있다.

- getSignature(): 원형정보, method 모양
- getArgs(): 인자(parameter)정보, Object[] 반환

📌. (ProceedingJoinPoint) joinPoint.proceed();
// target method Invocation, 실제 핵심기능을 담당하는 method 호출, 사전과 사후의 기준

AOP 프록시 서버에게 위빙(위임)한다


"join point가 호출될 때, 적절하게 advice를 삽입해줘라"

🚀. AOP 설정 [springAOP.xml]
⚠️. 적용방법(XML기반 / Java기반)

  <aop:config>
       <aop:aspect id="aspect" ref="advice">
        <aop:around method="around" pointcut="execution(public * exam.service.*Impl.*ello(..))" />
    </aop:aspect>  
  </aop:config> 

  <!-- AspectJ(어노테이션 이용)시 -->
  <aop:aspectj-autoproxy />

  ⚠️ J2SE방식 (인터페이스 有) vs CGLIB방식 (인터페이스 x)
  AOP proxy server 생성시 proxy-target-class="true"옵션은 CGLIB방식을 결정 (default: J2SE방식)

[MessageServiceImpl.java]

⚠️ MessageService 인터페이스 생략

  public class MessageServiceImpl implements MessageService {
      @Override
      public void korHello() {
          System.out.println("MessageServiceImpl의 korHello() 메서드 핵심로직입니다...");
          try {
              Thread.sleep(1000);
          } catch(InterruptedException e) {
              e.printStackTrace();
          }
      }

      @Override
      public void engHello() {
          System.out.println("MessageServiceImpl의 engHello(String name) 메서드 핵심로직입니다...");
          try {
              Thread.sleep(1000);
          } catch(InterruptedException e) {
              e.printStackTrace();
          }
      }
  }

[MainApp.java]

  public class MainApp {
      public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("springAOP.xml");

          MessageService service = context.getBean("target", MessageService.class);
          service.engHello();
          System.out.println("-----------------------------");
          service.korHello();
          System.out.println("-----------------------------");
      }
  }
Comments