본문 바로가기
프로그래밍/스프링 & 스프링 부트

스프링 AOP란? - (AOP적용 예제)

by 밍구몬 2019. 6. 18.

AOP ( Aspect-Oriented Programing)

aop는 관점 지향 프로그래밍 이라는 의미로 번역된다. 

aop를 이용하면 기존의 코드에 첨삭 없이, 메소드의 호출 이전 또는 이후에 필요한 로직을 수행하는 방법을 제공한다.

관점이라는 용어는 개발자들에게는 관심사라는 말로 통용된다. 관심사는 개발 시 피룡한 고민이나 염두에 두어야 하는 일이라고 볼 수 있다.

ex)

파라미터가 올바르게 들어왔는가?

적절한 권한을 가지고 있는가?

 

JoinPoint

 

관심사와 비즈니스 로직이 결합되는 지점을 조인 포인트라고 한다.

ex) 메소드가 실행되기 전 또는 실행된 후

 

Pointcut

 

관심사와 비즈니스 로직이 결합되는 지점을 결정하는 것을 포인트컷이라고 한다.

구분 설명
execution(@execution) 메소드를 기준으로 Pointcut을 설정
within(@within) 특정한 타입(클래스)을 기준으로 Pointcut을 설정
this 주어진 인터페이스를 구현한 객체를 대상으로 Pointcut을 설정
args(@args) 특정한 파라미터를 가지는 대상들만을 PointCut으로 설정
@annotation 특정한 어노테이션이 적용된 대상들만을 Pointcut으로 설정

 

Aspect와 Advice

 

Aspect는 조금 추상적인 개념을 의미한다. 관심사 자체를 의미하는 추상명사라 볼 수 있고,

Advice는 Aspect를 구현한 코드이다.

구분 설명
Before Advice Target의 JoinPoint를 호출하기 전에 실행되는 코드로 코드의 실행 자체에는 관여할 수 없다.
After Returning Advice 모든 실행이 정상적으로 이루어진 후에 동작하는 코드.
After Throwing Advice 예외가 발생한 뒤에 동작하는 코드.
After Advice 정상적으로 실행되거나 예외가 발생했을 때 구분 없이 실행되는 코드.
Around Advice

메소드의 실행 자체를 제어할 수 있는 가장 강력한 코드.

직접 대상 메소드를 호출하고 결과나 예외를 처리할 수 있다.

 

AOP 적용 예제

스프링 AOP는 AspectJ라는 라이브러리의 도움을 많이 받는다.

현재 스프링 버전을 5.x으로 맞추었기 때문에 AspectJ의 버전을 1.9.4로 높여 주었다.

 

pom.xml AjpectJ 설정

	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.9.4</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>

pom.xml lombok 라이브러리 추가

		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.8</version>
		</dependency>

pom.xml AspectJ Weaver 라이브러리 추가 

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>
        
		<!-- AspectJ weaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

 

AOP 설정

root-context.xml 에서 namespaces를 클릭 후 aop와 context를 추가하고, servlet-context.xml에도 똑같이 해준다.

root-context.xml과 servlet-contetx.xml

	<context:component-scan base-package="com.wipia.study"></context:component-scan>
	
	<!-- @Aspect 어노테이션을 통해 어드바이스가 동작 하도록 -->
	<aop:aspectj-autoproxy />

 

Advice 작성

@Aspect : 해당 클래스의 객체가 Aspect를 구현한 것이라는걸 나타내기 위해 사용.

@Component : AOP와 관계가 없지만 스프링에서 bean으로 인식하기 위해서 사용.

package com.wipia.study.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Advice {
	
	/*
	 * Before : 클래스의 메소드 실행 전
	 * within : BoardController 클래스를 지정
	 */
	@Before("within (com.wipia.study.controller.BoardController)")
	public void beforeAdvice() {
		System.out.println("BoardController Before");
	}
	
	/*
	 * After : 메소드 실행 후
	 * execution : getBoardList 메소드 지정 * 로 모든 메소드 지정 가능
	 * 접근지정자 : 생략 가능 ex) public, private
	 * * : 변환 타입
	 * 
	 */
	@After("execution(* com.wipia.study.controller.BoardController.getBoardList(..))")
	public void afterAdvice() {
		System.out.println("after getBoardList");
	}
	
	/*
	 * AfterThrowing : 예외 발생 시
	 * 모든 클래스에서 메소드 호출 에러가 발생했을 때 동작
	 */
	@AfterThrowing(pointcut="execution(* com.wipia*..*.*(..))", throwing="e")
	public void afterThrowingAdvice(Exception e) {
		System.out.println("에러다 : "+e);
	}
	
	/*
	 * 모든 메소드 실행시 얼마나 걸리는지 시간 출력
	 */
	@Around("execution (* com.wipia..*.*(..))")
	public Object time(ProceedingJoinPoint pjp) {
		
		long start = System.currentTimeMillis();
		
		System.out.println("--- Target : "+pjp.getTarget());
		System.out.println("--- Parameter : "+Arrays.toString(pjp.getArgs()));
		
		Object result = null;
		
		try {
			result=pjp.proceed();
		}catch (Throwable e) {
			e.printStackTrace();
		}
		
		long end = System.currentTimeMillis();
		System.out.println("--- Time : "+(end-start));
		
		return result;
	}
	
}