Programming/Spring framework

AOP 프로그래밍, 프록시에 대하여. 2021-12-16

최동훈1 2021. 12. 16. 17:53

우선적으로 AOP 란 Aspect Oriented Programing 의 약자이다. 

Aspect 란 측면이라는 뜻으로, 좁은 부분을 뜻한다.

즉, 측면 지향 프로그래밍 이란 것은, 프로그램상의 어느 한정적인 부분들을 나누어서 프로그래밍하는 것을 의미한다.

 

이 한정적인 부분을 공통부분이라 칭하고  핵심부분과 구분되어 별도시스템이 동작 가능하게 한다.

아래의 코드를 보자.

public class RecCalculator implements Calculator{

	@Override
	public long factorial(long num) {
		// TODO Auto-generated method stub
		if(num==1) {
			return 1;
		}
		
		return num*factorial(num-1);
	}
	

}

위 코드는 계승을 구하는 프로그램이다.

즉 핵심기능이다. 만약 이 기능에 실행시간을 재는 기능을 넣고싶다면 아래의 코드를 프록시로 스프링 설정클래스에 등록하면 된다.

이렇게 별도로 공통의 기능을 적용시키는 이유는, "계승을 구한다"는 핵심기능의 구현과 "계승을 구하는데 걸리는 시간측정" 이라는 공통의 기능 구현을 완전히 분리시키기 위함이고, 객체지향에 입각하여서 독립성을 보장하기 위함이다. 이로인해 유지보수성이 극도로 올라가게 된다.

 

import java.util.Arrays;

import org.aspectj.apache.bcel.classfile.Signature;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class ExeTimeAspect {
	@Pointcut("execution(public * chap07..*(..))")
	public void pointcut() {
		
	}
	@Around("pointcut()")
	public Object measure(ProceedingJoinPoint joinPoint) throws Throwable{
		long ans;
		long start=System.currentTimeMillis();
		try {
			Object result=joinPoint.proceed();
			return result;
		}finally {
			long end=System.currentTimeMillis();
			org.aspectj.lang.Signature sig = joinPoint.getSignature();
			System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
					joinPoint.getTarget().getClass().getSimpleName(),
					sig.getName(), Arrays.toString(joinPoint.getArgs()),
					(end - start));
		}
		
	}
	

}

위 코드에서, 스프링에 등록록되어 공통의 기능을 수행하는 객체를 "프록시 객체" 라 하고 실체적으로 공통의 기능이 적용되는 객체를 "프록시 대상 객체" 라고 한다. 위 사례에서 프록시 객체는 ExeTimeAspect 이고, 프록시 대상객체는 Calculator 이다.

 

프록시 객체를 스프링에 등록하기 위해서 필수적인 애너테이션이 있다.

 

1. @Aspect 는 공통의 기능을 구현한 "클래스" 에 붙여서 스프링 에 알려준다. 말 그대로 Aspect 를 칭한다.

 

2. @Pointcut은 어느부분에 Aspect를 적용시킬지 범위를 정해주는 애너테이션이다. Aspect 클래스의 매서드위에 달고, 범위는 execution으로 나타낸다. 위는 chap07패키지의 모든 public 매서드에 적용됨 이다.

 

또 이 @Pointcut이 적용된 메서드를 @Around의 인자로 주면서, @Around가 붙은 매서드가 어느곳에 적용될지 알려준다.

 

3. @Around 는 Aspect 클래스 안에서 실질적인 공통의 기능을 구현한 "메서드"에 붙인다. 프록시대상객체의 메서드 호출 전, 후, 둘다 등 다양한 범위가 있지만 둘다(앞 뒤 모두 사용)인 Around 가 가장 많이 쓰인다. Advice의 범위를 정해주는 거다. 

 

이런 과정을 거쳐서 Aspect 클래스 가 만들어 졌다면, 이 Aspect클래스를 스프링 설정 클래스를 통해 스프링이 관리하는 프록시 객체로 등록해야 한다.

@Configuration
@EnableAspectJAutoProxy
public class Appctx2 {
	@Bean
	public Calculator calculator() {
		//만약 프록시 대상 객체가 인터페이스를 상속 받는다면,
		//스프링이 만들어주는 프록시도 같은 Calculator 인터페이스를 
		//상속받는다.
		return new RecCalculator();
	}
	@Bean
	public ExeTimeAspect exetime() {
		return new ExeTimeAspect();
	}
	

}

Appctx 프록시 설정 클래스에 등록한 모습.

 

또 한 Pointcut에 여러가지 Aspect가 적용되어 실행될 수 있다. 이 경우, Aspect가 적용되는 순서는 랜덤인다. @Order 애너테이션을 이용해서 순서를 지정해 줄 수도 있다. @Order의 인자는 "숫자"로 오고 수가 작은 Aspect가 먼저 실행된다.

@Order은 @Aspect 애너테이션 바로 밑에 붙인다.

 

공부시간 2시간 35분.

순공시간 2시간.

 

오늘은 모더나 3차 백신맞고 삼산동 스벅에서 3시 15분부터 공부해서 5시 52분까지 공부했다. 그래도 나름 책에서 제일 어려운 장이라고 하는 AOP 부분을 다 이해하고 백지에 설명할수 있을 정도로 공부 해놔서 기분은 좋다.