본문 바로가기

공부/Spring

[Spring] Chapter 07 - Spring AOP

 

Chapter 7 내용

  • 프록시와 AOP
  • 스프링 AOP 구현

 

AOP( Aspect Oriented Programming)


 

- AOP는 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법이다

- AOP는 핵심 기능과 공통 기능의 구현을 분리해서 수정 없이 공통 기능을 적용할수 있게 한다.

 

 

- AOP 기능을 사용하기 위해 의존 관계를 추가  (intellij + gradle + AOP)

implementation group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.13'

 

  public interface Calculator {

      public long factorial(long num);

  }

  public class RecCalculator implements Calculator {

        @Override
        public long factorial(long num) {
            long start = System.currentTimeMillis();
            try {
                if (num == 0)
                    return 1;
                else
                    return num * factorial(num - 1);
            }finally {
                long end = System.currentTimeMillis();
                System.out.printf("RecCalculator(%d) 실행시간 %d \n",num,(end-start));
                //start와 end 시간을 구해서 차이를 구하면 된다.
            }

        }

    }

    import chap07.test.RecCalculator;

    public class Main {
        public static void main(String[] args) {
            RecCalculator ttCal1 = new RecCalculator( );
            ttCal1.factorial(3);
        }
    }

 

위의 코드에서 보면 재귀 구현 함수의 실행 시간을 출력하고 싶으면, start시점과 end시점의 시간을 구해서 차이를 알면된다. 하지만, 위의 코드에 출력문을 넣으면 메시지가 반복으로 출력되는 문제가 있다. 

 

 

[결과]

출력문이 반복으로 출력됌

 

프록시(Proxy)


 

- 핵심 기능의 실행은 다른 객체가 하고, 부가적인 기능만을 제공하는 객체

- 핵심 기능은 구현하지 않으며 대신 여러 객체에 공통으로 적용할 수 있는 기능을 구현

 

    public class ExeTimeCalculator implements Calculator {

        private Calculator delegate;

        public ExeTimeCalculator(Calculator delegate) {
            this.delegate = delegate;
        }

        @Override
        public long factorial(long num) {
            long start = System.nanoTime();
            long result = delegate.factorial(num);
            long end = System.nanoTime();
            System.out.printf("(%d) 실행 시간 = %d\n",
                    delegate.getClass().getSimpleName(),
                    num, (end - start));
            return result;
        }

    }

   public class Main2 {
        public static void main(String[] args) {
            ExeTimeCalculator ttCal1 = new ExeTimeCalculator(new ImpeCalculator());
            System.out.println(ttCal1.factorial(20));

            ExeTimeCalculator ttCal2 = new ExeTimeCalculator(new RecCalculator());
            System.out.println(ttCal2.factorial(20));

        }
    }

 

[결과]

위 코드는 ExeTimeCalulator클래스에서 ImpleCalculator 객체, RecCalculator객체를 상속받아 실행한다.

 

  • Proxy : ExeTimeCalulator에서는 부가적인 기능 (실행시간 측정) 실행
  • 대상객체 : 핵심 기능의 실행은(factorial()기능)은 ImpleCalculator , RecCalculator 객체에서 실행

 

AOP


AOP는 핵심 기능의 코드를 수정하지 않으면서 공통 기능의 구현을 추가하는 것이다.

 

  • 컴파일 시점에 코드에 공통 기능을 삽입
  • 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입
  • 런타임에 프록시 객체를 생성해서 공통 기능을 삽입 (스프링이 제공하는 방식)

 

AOP 주요 용어

  • Aspect : 여러 객체에 공통으로 적용되는 기능 (트랙잭션, 보안 등)
  • Advice : 언제 공통 관심 기능을 핵심 로직에 적용할지 정의

         - Before Advice : 대상 객체의 메서드 호출 전에 공통 기능 호출 

         - After Returning Advice : 대상 객체의 메서드가 익셉션 없이 실행된 이후에 공통 기능을 실행

         - After Throwing Advice : 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우 공통 기능 실행

         - After Advice : 익셉션 발생 여부에 상관없이 실행 후 공통 기능 실행

         - Arount Advice : 대상 객체의 메서드 실행 전, 후 또는 익셉션 발생 시점에 공통 기능 실행

 

  • Joinpoint: Advice를 적용 가능한 지점
  • Pointcut : Joinpont의 부분 집합, 실제 Advice가 적용되는 Joinpoint
  • Weaving: Advice를 핵심 로직 코드에 적용하는 것

 

    @Aspect //Advice와 Pontcut을 함께 제공
    public class ExeTimeAspect {

        @Pointcut("execution(public * chap07..*(..))") //공통 기능을 적용할 대상
        private void publicTarget() {
        }

        @Around("publicTarget()") //공통 기능을 적용한다는 것을 의미, publicTarget메서드는 @Around가 붙은 measure()메서드를 적용한다.
        public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {// ProceedingJoinPoint 파라미터는 프록시 대상 객체의 메서드를 호출할 때 사용
            long start = System.nanoTime();
            try {
                Object result = joinPoint.proceed();
                return result;
            } finally {
                long finish = System.nanoTime();
                Signature sig = joinPoint.getSignature();
                System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
                        joinPoint.getTarget().getClass().getSimpleName(),
                        sig.getName(), Arrays.toString(joinPoint.getArgs()),
                        (finish - start));
            }
        }

    }
    @Configuration
    @EnableAspectJAutoProxy // @Aspect를 공통 기능으로 적용하기 위해 설정 클래스에 필요함
    public class AppCtx {

        @Bean
        public ExeTimeAspect exeTimeAspect() {
            return new ExeTimeAspect();
        }

        @Bean
        public Calculator calculator() {
            return new RecCalculator();
        }

    }
    public class MainAspect {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx =
                    new AnnotationConfigApplicationContext(AppCtx.class);

            Calculator cal = ctx.getBean("calculator", Calculator.class);
            long fiveFact = cal.factorial(5);
            System.out.println("cal.factorial(5) = " + fiveFact);
            System.out.println(cal.getClass().getName());
            ctx.close();
        }

    }

 

AOP를 구현하는 코드를 작성 후 돌려보면,

 

 

 

REFERENCE


 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문(최범균)

300x250