AOP란 무엇일까? - 02

AOP의 핵심 용어 정리

  • Target(타겟) : 공통 기능이 부여될 대상을 의미합니다. 클래스 혹은 메소드가 될 수 있습니다.
  • Aspect(애스팩트) : 여러 객체에 적용될 공통 기능을 의미합니다. ex) 보안, 로그, DB처리
  • Advice(어드바이스) : 공통 기능이 부여될 시점을 의미합니다. ex) 메소드 호출 전, 메소드 실행 이후
  • JoinPoint(조인포인트) : Advice가 적용 될 수 있는 지점을 의미하며 스프링의 경우 프록시 방식의 메소드 조인포인트를 지원합니다. 다른 AOP 방식의 경우 필드에도 적용이 가능하다.
  • PointCut(포인트컷) : 여러 개의 조인 포인트를 모아 포인트컷이라 부릅니다.

AOP Advice의 종류

  • <aop:before> : 메소드가 실행되기 전에 실행
  • <aop:after-returning> : 메소드가 정상 실행 된 후 실행
  • <aop:after-throwing> : 메소드 실행 중 Exception 발생 시 실행
  • <aop:after> : 메소드 실행 중 Exception이 발생하여도 실행
  • <aop:around> : 메소드 실행 전 1회, 실행 후 1회 및 Exception 발생 시 실행
  • 스프링 AOP에서는 주로 around advicebefore를 사용한다.

스프링에 AOP 구현하기

앞서 살펴본 바와 같이 스프링은 프록시를 이용한 AOP를 제공한다.
이 방법에는 POJO 클래스를 이용한 XML 적용어노테이션(@Aspect)으로 두 가지가 있다.

  • POJO(Plain Old Java Object) : 말 그대로 해석을 하면 오래된 방식의 간단한 자바 오브젝트라는 말로서 Java EE 등의 중량 프레임워크들을 사용하게 되면서 해당 프레임워크에 종속된 “무거운” 객체를 만들게 된 것에 반발해서 사용되게 된 용어이다. (출처 : 위키피디아)

1. XML을 이용한 스프링 AOP

XML을 이용한 스프링 AOP를 사용할 경우, Aspect를 어디에 언제 적용할지를 설정한다.
이때 Aspect 클래스는 POJO(순수 자바 객체)이다.

적용을 위한 작업 순서

  • Dependency 설정

    pom.xml에 의존 라이브러리(aspectj) 추가
    
    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.2</version>
    </dependency>
  • 공통 코드(Proxy Code) 작성

    Aspect 코드 구현
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public Object toLog(ProceedingJoinPoint joinpoint) throws Throwable {
    String methodName = joinpoint.getSignature().getName(); // 메소드명
    long startTime = System.nanoTime(); // 시작시간
    try {
    Object obj = joinpoint.proceed(); // 핵심 기능 실행
    return obj;
    } finally {
    long endTime = System.nanoTime(); // 종료시간
    System.out.println("[" + methodName + "] is " + (endTime - startTime));
    }
    }

Aspect 코드 구현 시, 스프링 AOP에서 가장 많이 사용되는 Advice인 Around Advice로 구현할 경우 ProceedingJoinPoint의 proceed()를 통해 핵심 기능을 실행시킨다.

Around Advice 생명주기

  • config.xml 설정
    AOP 설정 파일에 Aspect 빈 등록 및 PointCut 설정
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--  공통 기능을 제공할 클래스를 빈에 등록처리 -->
    <bean id="logAct" class="com.jhdev.asfect.LogAspect" />

    <!-- Aspect 설정: Advice를 어떤 Pointcut에 적용할 지 설정 -->
    <aop:config>
    <aop:aspect ref="logAct">
    <aop:around pointcut="execution(* com.jhdev.board.service..*Impl.*(..))" method="toLog"/>
    </aop:aspect>
    </aop:config>

2. 어노테이션을 이용한 스프링 AOP

어노테이션을 이용한 AOP 설정은 XML을 이용한 방법과 유사하지만 비교적 더 간단하다.
공통 기능 클래스에 어노테이션과 함께 PointCut을 설정한다.

  • 어노테이션을 이용하는 방법을 추천한다. 간결하고 생산성이 빠를 수 있다.

적용을 위한 작업 순서

  • Dependency 설정

    pom.xml에 의존 라이브러리(aspectj) 추가
    
    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.2</version>
    </dependency>
  • 공통 코드(Proxy Code) 작성

    Aspect 코드 구현
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Aspect	// Aspect Anotation
    public class LogAspect {
    // pointcut 설정
    @Pointcut("within(com.jhdev.board.service.*)")
    private void pointcutMethod() {
    }

    // pointcut 메소드 설정
    @Around("pointcutMethod()")
    public Object toLog(ProceedingJoinPoint joinpoint) throws Throwable {
    String methodName = joinpoint.getSignature().getName(); // 메소드명
    long startTime = System.nanoTime(); // 시작시간
    try {
    Object obj = joinpoint.proceed(); // 핵심 기능 실행
    return obj;
    } finally {
    long endTime = System.nanoTime(); // 종료시간
    System.out.println("[" + methodName + "] is " + (endTime - startTime));
    }
    }

    // pointcut 직접 설정
    @Before("execution(public * com.jhdev.board.service..*(..))")
    public void beforeTest() {
    System.out.println("Before Advice");
    }

Aspect 코드 구현 시, XML POJO 방식과 대부분 동일하지만 다른 점은 pointcut과 advice 설정을 Aspect 구현 클래스에서 한다.

  • config.xml 설정
    AOP를 xml에 설정하던 것과 다르게 빈과 autoproxy만을 설정
    
1
2
3
4
5
<!--  공통 기능을 제공할 클래스를 빈에 등록처리 -->
<bean id="logAct" class="com.jhdev.asfect.LogAspect" />

<!-- AutoProxy 설정-->
<aop:aspectj-autoproxy />

AspectJ의 PointCut 표현 방식

AspectJ 사용 시 PointCut을 명시하는 방법에 대해 알아보자
이를 AspectJ 문법이라고 부른다.
AOP는 다양한 핵심 기능에 공통 기능을 적용해야하기 때문에 AspectJ 문법으로 범위를 만들어야 한다.

기본 문법

* : 모든 클래스 혹은 메소드
. : 현재 패키지
.. : 0개 이상

AspectJ PointCut 명시자

execution : [접근명시자패턴] 리턴타입패턴 이름패턴(파라미터패턴) 형태로 세밀한 명시가 가능
within : 특정 메소드가 아닌 특정 패키지 하위에 모든 메소드을 명시하는 경우

Execution

execution([접근명시자패턴] 리턴타입패턴 이름패턴(파라미터패턴))

execution(publid void set*(..))
리턴 타입이 void이면서 메소드명이 set으로 시작하는 경우

execution(* com.jhdev.board.service.*.*())
해당 패키지 내 클래스 중 파라미터가 없는 메소드

execution(* com.jhdev.board.service..*.*())
해당 패키지와 해당 패키지 하위에 존재하는 패키지 내 파라미터가 없는 메소드

WithIn

within(com.jhdev.board.service.*())
해당 패키지 내 모든 클래스의 메소드

within(com.jhdev.board.service..*())
해당 패키지와 해당 패키지 하위에 존재하는 패키지 내 모든 클래스의 메소드

Share