springframework

o.s.w IoC 핵심기술. @AOP

Jungsoomin :) 2020. 9. 19. 14:46

스프링 에노테이션기반 aop를 학습한다.

스프링 부트기반에서 스프링 aop를 사용하기위해서

 

spring-boot-starter-aop 의존이필요하다.

  1. spring-aop

  2. aspectj-weaver

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

사용 방법..?

 

공통기능 클래스에는 @Aspect공통기능 클래스임을 알리며 Bean 으로 등록해야만 한다.

필요한 정보는 다음과 같다.

  1. 해야할 일 : Advice

  2. 어디에 적용할 것인가 : PointCut

@ComponentScan 의 속성 값에 includeFilters 값을 주어 @Filter 에 어노테이션을 지정하여 @Aspect 를 스캔해도 무방하다.

@Aspect
@Component
public class PerfAspect {

}

 


핵심 객체와 표현식은..?

 

 Object 메서드명 (ProceedingJoinPoint) 의 메서드 시그니쳐를 가진 메서드를 작성하는데,

ProceedingJoinPoint Advice가 적용되는 대상이라고 보면 좋다.

  1. @Around : 메서드 실행 전, 후, 예외발생, 리턴 시 에 모두 적용하며 해당 메서드를 감싸는 느낌이다.

  2. @Before : 메서드 실행 전
  3. @AfterThrowing : 예외 송출
  4. @AfterReturning : 무사히 리턴시

이정도로 알고있는데 , 직접 적용해보긴 했으나, @Around 가 좋더라...라는 개인적 의견...토이프로젝트에서 했지만..

 

이전 글에 Signature 객체와 ProceedingJoinPoint 객체의 메서드를 기술했으니, 이번에는 생략하려한다.

 


익숙했던 Spring-Aop 기초 이론, 충격적이고 새로운 것은 명시자.

처음엔 AOP 기존 용어와  execution 명시자의 표현식을 외우기 위해 입에 붙여서 중얼거렸다. 

  1. public은 spring-aop 기준으로 반드시 설정되야하므로 생략해도 좋고...

  2. 리턴타입 | 패키지 | 클래스 | 메서드명 (파라미터) 로 정의할 수 있다.

  3. * 은 모든 것을 의미하며 ..은 0개 이상을 의미한다. . 은 하나 구체적으로 파라미터를 명시하거나 조합할 수 있던 걸로 기억한다.

  • 이 것이 참 오묘...했던게, 내가 원치않는 메서드까지 aop 적용대상이 되는 경우가 있더라. 그리고 이번기회에 새로운 명시자를 배우게 되어 기뻤다.

  • @annotation 명시자는 해당 어노테이션의 이름을 속성 값으로 주어 어노테이션이 적용된 joinPoint에만 Advice를 적용한다.

  • bean 명시자빈의 identifier 를 속성 값으로 주어 해당 빈의 모든 public 메서드가 PointCut이 된다.

@Aspect
@Component
public class PerfAspect {

    @Around("execution(* me.soomin.demospring51.aop.SimpleEvent.*(..))")
    public Object logPerf1(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        Object retVal = pjp.proceed();

        System.out.println(System.currentTimeMillis() - start);

        return retVal;
    }

    @Around("@annotation(PerfLogging)")
    public Object logPerf2(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        Object retVal = pjp.proceed();

        System.out.println(System.currentTimeMillis() - start);

        return retVal;
    }

    @Around("bean(simpleEvent)")
    public Object logPerf3(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        Object retVal = pjp.proceed();

        System.out.println(System.currentTimeMillis() - start);

        return retVal;
    }


    @Before("bean(simpleEvent)")
    public void hello(){
        System.out.println("Hello");
    }


}

 

기억을 되살려보려 포인트컷 재사용 방법을 다시 확인.

 @Pointcut("@annotation(PerfLogging)")
    public void loggingPoint(){
        
    }
    
    @Around("loggingPoint()")
    public Object logPerf2(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        Object retVal = pjp.proceed();

        System.out.println(System.currentTimeMillis() - start);

        return retVal;
    }

주목했던 명시자는 @annotation 명시자.

일단, @annotation 명시자는 직접 정의한 어노테이션을 적용할 수 있다는 점이 놀라웠다.

 

어노테이션을 만들때 주의할 점 은 이런 부분이라고 하신다.

 

  1. @Retention 의 속성에 RetentionPolicy를 CLASS 이상으로 줘야한다는 점.

  2. RetentionPolicy에노테이션 정보를 얼마나 유지할 것인가 이다. / default = RetentionPolicy.CLASS 

  3. RetentionPolicy.CLASS 는 컴파일 후의 바이트 코드에서도 에노테이션 정보를 남기겠다는 것이다.

  4. RetentionPolicy.SOURCE , 컴파일 후에 노테이션 정보는 사라진다. 그렇기에 어노테이션 명시자에 적합하지 않다.

/**
 * 이 에노테이션을 사용하면 성능을 로깅해줍니다.
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface PerfLogging {
}

 

 

포인트컷 조합식으로는 && , || , ! 이 있다. ..처음봐서..너무 놀랍다.

    @Pointcut("@annotation(PerfLogging)&&@annotation(Deprecated)")
    public void loggingPoint(){

    }