카테고리 없음

[초보 웹 개발자를 위한 스프링5 프로그래밍 입문] 07. AOP 프로그래밍

## 설정법

spring-context, aspectjweaver 모듈 의존성 추가

```

public class ReculsiveCalculator implements Calculator {

   @Override

    public long factorial(long num) {

         if (num = 0)

            return 1;

        else

           return num * factorial(num - 1);

     }

}

```

 

코드가 있다고 할 때 이 함수의 실행시간을 알아내려면 어떻게 해야 하는가? 함수 앞뒤에 모두 시간을 출력하는 라인을 추가해야 한다.

수정사항이 있을 때마다 반복해야한다.

 

딱 한번만 수정하기 위해 프록시의 개념이 등장한다.

```

public class TimeReculsiveCalculator implements Calculator {

   private Calculator delegate;

 

   public TimeReculsiveCalculator(Calculator delegate) {

      this.delegate = delegete;

   }

 

   @Override

    public long factorial(long num) {

         long start = System.nanoTime();

         long result = delegate.factorial(num);

         long end = System.nanoTime();

         System.out.printf("%s.factorial(%d) 실행 시간 = %d\n", delegate.getClass().getSimpleName(), num, (end-start));

         return result;

     }

}

```

 

이로써 기존 코드 수정 없이 시간을 구할 수 있게 되었다.

엄밀히 말하면 프록시보다는 데코레이터 개념에 가깝다.

proxy: 접근 제어 관점에 초점

decorator: 기능 추가와 확장에 초점

 

프록시의 특징은 핵심 기능은 구현하지 않는다는 것

 

## AOP

Aspect Oriented Programming. 핵심 기능과 공통 기능을 분리하는 것

 

구현 방법은 세 가지이다.

1. 컴파일 시점에 코드에 공통 기능 삽입

2. 클래스 로딩 시점에 바이트 코드에 공통 기능 삽입

3. 런타임에 프록시 객체를 생성해 공통 기능 삽입

 

1, 2번은 spring AOP에서 지원하지 않는다. 원할 경우 AspectJ를 사용하면 가능하다.

 

호출 순서는 다음과 같다.

client -> AOP proxy -> 공통 기능 모듈, 실제 비즈니스 객체

 

스프링 AOP는 프록시를 자동으로 만들어주므로 직접 구성할 필요 없이, 공통 기능 클래스만 구현하면 된다.

 

## 스프링 AOP 구현

* @Aspect를 붙인다.

* @Pointcut 애노테이션으로 공통 기능을 적용할 Pointcut을 정의한다.

* 공통 기능을 구현한 메서드에 @Around 애노테이션을 적용한다.

 

ex) 

```

@Aspect 

public class ExeTimeAspect {

   @Pointcut("execution(public * chap07..*(..))")  // chap07와 하위 패키지 public method에 적용

    private void publicTarget() {}

 

    @Around("publicTarget()")  // 메서드의 시작과 끝에 적용

    public Object measure(ProceedingJointPoint joinPoint) throws throwable {   // ProceedingJointPoint: 대상 메서드 호출 시 사용

        ...

        joinpoint.proceed(); // 대상 메서드 호출

        ...

    }

}

```

 

## 프록시 생성 방식

빈 객체가 인터페이스를 상속하면 기본적으로 프록시는 그 인터페이스를 상속한다. 

그렇기 때문에 아래 코드는 BeanNotOfRequiredTypeException 오류를 발생한다.

applicationContext.getBean("calculator", ReculsiveCalculator);

인터페이스인 applicationContext.getBean("calculator", Calculator); 로 가져와야 한다.

 

### excution 명시자 표현식

앞서 Aspect를 적용할 위치를 지정할 때 사용한 @Pointcut 설정에서 excution 명시자를 사용했다.

패턴은 다음과 같다.

execution(수식어패턴? 리턴타입패턴 클래스이름패턴?메서드이름패턴(파라미터패턴))

수식어패턴은 생략가능하며 public, private 등이 온다. 각 패턴은 *을 사용하여 모든 값을 표현하며, 점 두개(..)를 이용하여 0개 이상을 표현한다.

```

//예시

execution(public void set*(..))

```

 

## Advice 적용 순서

한 클래스에 두 개 이상의 프록시(어드바이스)가 적용된다면, 그 순서는 자바나 스프링 프레임워크 버전에 따라 달라진다. 그러므로 순서가 중요하다면 @Order 애노테이션을 통해 직접 지정해줄 수 있다.

@Order(숫자)는 숫자의 값이 작은 것부터 큰 것 순으로 Advice를 적용한다.

```

//예시

@Order(1)

@Aspect

public class ExeTimeAspect { 

}

```