본문으로 바로가기

[Spring] AOP 총정리 및 예제

category 1. 웹개발/1_2_2 Spring Framework 2020. 11. 29. 18:49

    

 

1. AOP(Aspect Oriented Programming)란?

공통 관심 기능을 분리하여 반복되는 부분을 추출해 핵심 로직에 영향을 미치지 않고 소스의 중복을 줄이는 방법으로

기존 OOP에서 공통 관심 기능을 여러 모듈에서 적용하며 발생하는 중복된 코드 양산의 한계를 극복하기 위해 나오게 되었습니다.

즉. 핵심 로직에 집중할 수 있도록, 필요하지만 중복해서 작성해야 하는 핵심 로직 이외의 코드들은 외부로 빼놓는 것입니다.

 

또한, AOP는 흩어진 Aspect 들을 모아서 모듈화 하는 기법입니다.

*Aspect란?

- 객체지향 언어의 클래스와 비슷한 개념이라고 생각하면 쉽습니다.

- 그 자체로 애플리케이션의 도메인 로직을 담은 핵심기능은 아니지만, 많은 오브젝트에 걸쳐서 필요한 부가기능을 추상화해놓은 것입니다.

 

 

2. 용어

AOP에서는 핵심 로직에서 직접 공통관심기능을 호출하지 않고 위빙(Weaving)이라 불리는 작업을 해서 공통 관심 기능 코드를 삽입합니다.

이런 위빙을 하기위해서는 어디에, 언제 삽입할지 알아야 합니다.

이것을 Pointcut(어디에), Advice(언제)라고 합니다. 

 

1) Pointcut(포인트컷)

어디에 공통 관심 기능 소스를 삽입할 것인지 정의합니다.

하나의 @Aspect 클래스 안에 여러 개의 포인트컷을 선언할 수도 있습니다.

 

 

 

2) Advice(어드바이스)

언제 공통 관심 기능을 핵심 로직에 적용할지를 정의합니다.

어드바이스도 포인트컷과 마찬가지로 하나 이상을 정의할 수 있습니다.

어드바이스의 애노테이션에는 이에 적용할 포인트컷을 명시해야 합니다.

 

 

포인트컷에 이름을 부여해서 따로 정의하는 대신 어드바이스 메소드 애노테이션에 포인트컷 표현식을 넣어서 적용하는 방법도 있습니다.

 

 

@AspectJ에서는 다섯 가지 종류의 어드바이스를 사용할 수 있습니다.
(1) 메서드 실행 전 (before)
(2) 메서드 실행 후 (after)
(3) 반환된 후 (AfterReturning)
(4) 예외가 던져지는 시점 (AfterThrowing)
(5) 메서드 호출되는 전 과정 (around) : 실제로 가장 많이 쓰이고 매개변수로 ProceedingJoinPoint를 받음 

 

 

3. 실행되는 시점의 시간을 로깅하는 예제

이제 실제로 어떻게 활용할 수 있을지 메서드가 실행될 때 실행되는 시점의 시간을  로깅해보도록 하겠습니다.

자바 및 스프링에서는 Date라는 객체를 제공해주는데, 이 객체를 이용하여 진행하겠습니다.

 

LogGetDate.java 

package org.springframework.samples.petclinic.owner;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // Annotation을 런타임까지 사용
public @interface LogGetDate {

}

 

LogAspect.java

package org.springframework.samples.petclinic.owner;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
@Aspect
public class LogAspect {

	Logger logger = LoggerFactory.getLogger(LogAspect.class); // log

	@Around("@annotation(LogGetDate)") // method 실행 되는 전 과정
	public Object LogGetDate(ProceedingJoinPoint joinPoint) throws Throwable {
		Date date = new Date();
		date.getTime(); // 메소드가 실행되는 현재 시간

		Object ret = joinPoint.proceed();

		logger.info(date.toString());

		return ret;
	}

	// 포인트컷 선언의 예
	@Pointcut("execution(* hello(...))") // 포인트컷 파라미터는 없으며 표현식으로 정의 "execution ~"
	private void all() {

	}
}

 

OwnerController.java

@GetMapping("/owners/find")
@LogGetDate
public String initFindForm(Map<String, Object> model) {
    model.put("owner", new Owner());
    return "owners/findOwners";
}

 

실행 결과

 

 

메서드가 실행되니 로그에 잘 찍힌 것을 확인할 수 있습니다.

 

 

Reference

백기선님의 예제로 배우는 스프링 (개정판)