Programming/Spring framework

스프링 AOP의 기본 개념: Advice, joinpoint,Pointcut,Aspect 2021-06-07

최동훈1 2021. 6. 8. 01:29

우선 이전 포스트에서 AOP(Aspect oriented programing)이란 공통의 기능과 핵심기능의 분리를 통해 재사용성을 높여주는 프로그래밍 기법이라고 설명하였고, 스프링은 AOP를 프록시를 통해 구현한다고 설명하였다.

 

일반적인 AOP구현 방식은 총 3가지 방법이 있다.

1.컴파일 시점에 공통의 기능을 구현한 코드를 넣어준다.

2.클래스 로딩 시점에 바이트 코드에 공통의 기능을 구현한 코드를 넣어준다.

3.런타임 중 프록시 객체를 생성해서 공통의 기능을 삽입한다.

 

이 3가지 방법중 스프링에서 사용하는 방식은 3번이다. 스프링 AOP는 프록시 객체를 자동으로 만들어서 필요한 시점(PointCut)에 실행해준다. 그래서 공통기능을 구현한 클래스만 알맞게 구현하면 된다.

 

우선 스프링의 AOP의 용어들을 알아보자.

우선 Aspect는 영어사전적 의미로는 "관점,측면"이란 말이다. 즉, 클래스의 관점에따라(기능) 구별한다는 뜻이다.

Aspect: 여러객체에 공통으로 적용되는 기능.

 

Advice : 언제 공통의 기능을 실행할지, 설정한다. 예를들면, 대상 객체의 메서드 호출 앞 / 뒤 인지, 익셉션 일어나기 전/후 인지. 등을 설정해준다.

 

JoinPoint: Advice를 적용가능한 부분을 설정해 준다. 예를들면 필드값 변경, 메서드호출 등이다. 그런데 스프링은 프록시를 이용해서 AOP를 구현하기 때문에 메서드 호출에 대한 Joinpoint만 존재한다.

 

Pointcut: joinpoint의 부분집합으로, 실제 Advice가 적용되는 joinpoint를 나타낸다. 

 

*여기서 joinpoint, pointcut과 Advice의 관계가 이해가 안될수 있는데, Advice는 "언제" 공통의 기능을 실행가능한지 설정이고, pointcut은 "어디에" 공통의 기능을 적용할수 있는지(엄밀히말하면 Advice를 적용할수 있는곳) 설정하는 것이다.

 

*스프링은 프록시를 이용해서 메서드 호출시점에 Aspect(공통기능)을 적용하기 때문에, joinpoint,pointcut는 메서드 호출만 적용가능하다.

 

다음은 Advice의 종류에 대해서 설명하겠다. 대표적인 것으로는 

Before :대상객체의 메서드 호출 전.

After Returning :대상객체의  메서드가 익셉션 없이 실행된 후.

After Throwing :대상객체의 메서드가 익셉션이 발생한 경우.

After : 대상객체의 메서드의 익셉션 발생여부에 무관하게 메서드 실행 후.

Around : 대상객체의 메서드 호출 전/후, 익셉션 발생시.

 

등이 있는데 가장 많이 쓰이는 Advice는 Around이다. 이유는, 상기된 모든 조건에서 다양한 시점에 Aspect를 실행 가능하기 때문이다.

 

이젠 실제적으로, 스프링 AOP를 적용해 보자. 절차는 간단하다.

-공통의 기능을 구현할 클래스에 @Aspect 애너테이션을 붙인다. (@Aspect 애너테이션을 적용한 클래스는 Advice와 Pointcut을 함께 제공한다. 적어도 스프링은 Advice와 Pointcut이 있으리라 생각하고 찾는다.)

-@Pointcut 애너테이션으로 공통기능을 적용할 Pointcut ("어디에" Advice를 적용할수 있는지)를 설정한다.

-공통기능을 구현한 메서드에 @Around 애너테이션을 붙임으로서, Advice를 설정해준다.

 

-마지막으로 빈 객체 설정클래스(@Configuration 이 붙은 클래스)에 @EnableAspectJAutoProxy 애너테이션을 붙이면, 스프링은 @Aspect 애너테이션이 붙은 빈 객체를 찾아서 빈객체의 @Pointcut 설정과 @Around 설정을 이용한다.

 

실제적으로 @Aspect가 붙은 프록시를 만들어보자.

@Aspect
public class ExeTimeAspect {

	@Pointcut("execution(public * chap07..*(..))")
	private void publicTarget() {
	}

	@Around("publicTarget()")
	public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
		long start = System.nanoTime();
		try {
			Object result = joinPoint.proceed();
			return result;
		} finally {
			long finish = System.nanoTime();
			Signature sig = joinPoint.getSignature();
			System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
					joinPoint.getTarget().getClass().getSimpleName(),
					sig.getName(), Arrays.toString(joinPoint.getArgs()),
					(finish - start));
		}
	}

}

우선 @Aspect 애너테이션을 붙임으로서, 이 클래스는 공통의 기능을 수행하는 클래스라는 사실을 스프링에 알릴수 있다. 

또 @Pointcut을 통해 실제 프록시를 적용시킬 "범위"를 주었는데 애너테이션의 값으로 사용할 수 있는 execution 명시자에 대해선 다음 포스트를 통해 설명하겠다. 지금은 우선 "chap07패키지와 그 하위패키지에 위치한 타입의 퍼블릭 메서드를 Pointcut으로 설정한다". 정도로만 알면된다.

 

또 @Around 애너테이션을 통해 Around Advice를 설정한다. @Around 애너테이션의 값이 "publicTarget()"인데, 이는 publicTarget()메서드에 설정한 Pointcut의 범위에 Aspect를 적용시킨다는 것을 의미한다.

->쉽게 말해 그냥 Pointcut 설정을 Advice에 알려준다.

 

위, @Around("publicTarget()")을 쉽게 해석하면.

 @Around에 전달된 pointcut의 범위(publicTarget()에 설정한 범위)에 @Around가 붙여진 measure이라는 매서드(Aspect)를 적용시킨다 라는 뜻이다.

 

 

 

@Around가 적용된 measure메서드는 "대상 객체"를 파라미터로 받아, "대상객체의 메서드"를 proceed()라는 ProceedingJoinPoint에 오버라이딩된 메서드를 통해 호출한다. 그리고, 개발자는 대상객체의 메서드의 호출 전/후, 나 익셉션이 발생했을때(Around Advice의 범위), Aspect를 호출하도록 설정한다.

@Around("publicTarget()")
	public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
		long start = System.nanoTime();
		try {
			Object result = joinPoint.proceed();//대상객체의 매서드 호출
			return result;
		} finally {
			long finish = System.nanoTime();
			Signature sig = joinPoint.getSignature();
			System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
					joinPoint.getTarget().getClass().getSimpleName(),
					sig.getName(), Arrays.toString(joinPoint.getArgs()),
					(finish - start));
		}
	}

 

 

이 @Aspect를 붙인 완성된 공통기능을 적용하는데 필요한 클래스를 빈 객체에 등록시켜야 한다. 그래야 스프링이 @Aspect 애너테이션을 읽으며, 실질적인 AOP기능을 수행할 거니깐 말이다.

 

앞서 설명했듯이, @Configuration(빈 설정클래스)에 프록시를 등록할때는, @EnableAspectJAutoProxy 애너테이션을 꼭 붙여줘야 한다.

@Configuration
@EnableAspectJAutoProxy
public class AppCtx {
	@Bean
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}

	@Bean
	public Calculator calculator() {
		return new RecCalculator();
	}

}

  

 

*프록시: 핵심기능의 실행은 다른 객체에 위임하고, 부가적인 기능을 제공하는 객체

*대상객체: 실제 핵심 기능을 실행하는 객체

 

page163 공부완료.

 

 공부시간:1시간

순공부시간 30분.

 

오늘 오전에 외근으로 병원갔다온다고, 오전공부를 하나도 못했다. 그리고 오후엔 집중력이 떨어졌던거 같다. 나 스스로의 삶도 컨트롤하지 못하는데, 세상을 컨트롤할수 없고, 타인도 컨트롤 할수 없고, 내가원하는 목표도 컨트롤 할 수 없다.

나 스스로를 더욱 더 잘 컨트롤 하자.

오늘 운동마치고 크루엘라 라는 영화를 보았는데 디즈니에서 나온 영화치고는 매우 잘 만들어진거 같다.