참고 도서 : 스프링5 프로그래밍 입문 - 최범균 저 |
ch07. AOP 프로그래밍
[AOP 위한 프로젝트 준비]
-스프링이 AOP 구현 시 사용하는 모듈 aspectjweaver 추가해야 한다.
-메이븐 pom.xml 파일에 aspectjweaver 의존 추가해야 한다,
- aspectjweaver 모듈은 AOP 설정에 필요한 애노테이션 제공하므로 반드시 추가해야 한다.
스프링 프레임워크의 AOP 기능은 spring-aop모듈이 제공하지만, spring-context모듈을 의존 대상에 추가해놓음녀 sop모듈도 함께 의존대상에 포함되므로 spring-aop 모듈에 대한 의존을 따로 추가하지 않아도 된다. |
[a! 팩토리얼 재귀함수로 구현] : 실행 시간 구하기
-실행 시간 기본 매커니즘은 메소드의 실행 전, 후에 시간을 구한 뒤, 그 차이를 구하는 것
-단, 재귀호출로 구현한 경우 소스 코드 수정 -> 시간이 여러 번 출력됨
public class RecCalculator implements Calculator {
@Override
long start = System.currentTimeMillis();
try {
if (num == 0)
return 1;
else
return num * factorial(num - 1); //재귀 호출
}finally{
long end = System.currentTimeMillis();
System.out.printf(“%d), (end-start));
}
}
Q. 기존 코드 수정하지 않고 코드 중복도 피할 수 있는 방법 ? -> 프록시 객체 |
public class ExeTimeCalculator implements Calculator { //프록시 객체
//필드
private Calculator delegate;
//생성자
public ExeTimeCalculator(Calculator delegate) { //Calculator구현 객체를 인수로 줌
this.delegate = delegate;
}
//재정의
@Override
public long factorial(long num) {
long start = System.nanoTime(); //시작 시간 구함
long result = delegate.factorial(num);// <-인수로 받은 객체 메소드 실행 시간
long end = System.nanoTime(); //끝 시간 구함
System.out.printf("%s.factorial(%d) 실행 시간 = %d\n",
delegate.getClass().getSimpleName(),
num, (end - start)); //시간 차이 구함
return result;
}
}
[프록시]: proxy
-핵심 기능 실행은 다른 객체에 위임하고, 대상 객체 모두에 공통 적용 가능한 부가 기능을 제공하는 객체
-실제 핵심 기능 제공 객체 = 대상 객체
-대상 객체들에 공통 적용할 부가 기능 제공 객체 = 프록시
-> 이처럼 공통 기능 구현과 핵심 기능 구현을 분리하는 것이 AOP의 핵심이다. |
[AOP] : Aspect Oriented Programming
-여러 객체에 공통 적용될 기능을 따로 분리하여 재사용성 높여주는 프로그래밍 기법
-AOP는 핵심 기능과 공통 기능의 구현을 분리함으로써 핵심 기능 구현 코드 수정없이도
공통 기능 적용할 수 있게 만들어 준다.
->스프링도 프록시를 이용해서 AOP를 구현하고 있다. = 스프링 AOP AOP의 기본 = 핵심 기능에 공통 기능 삽입하는 것. |
[핵심기능에 공통 기능 삽입 방법 3가지]
➀ 컴파일 시점 -> 소스 코드에 공통 기능 삽입 ➁ 틀래스 로딩 시점 -> 바이트 코드에 공통 기능 삽입 ➂ 런타임 -> 프록시 객체 생성해서 공통 기능 삽입 <★스프링 제공 AOP 방식> |
[스프링 AOP]
프록시 객체를 자동으로 만들어준다.
프록시 클래스를 직접 구현할 필요 X
공통 기능 구현한 클래스만 알맞게 구현하면 됨
[AOP 주요 용어]
Advice | 언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의 |
Joinpoint | Advice를 적용 가능한 지점을 의미. 스프링은 프록시 이용해서 AOP 구현하므로 메소드 호출에 대한 Joinpoint만 지원 |
Pointcut | 실제 Advice가 적용되는 Jointpoint. Jointpoint의 부분집합 |
Weaving | Advice를 핵심 로직 코드에 적용하는 것 |
Aspect | 여러 객체에 공통으로 적용되는 기능 |
▶[Adivice의 종류]
-스프링은 프록시를 이용해서 메서드 호출 시점에 Aspect를 적용하므로
구현 가능한 Adviece 종류는 다음과 같다.
Before Advice | 대상 객체 메소드 호출 전 -> 공통 기능 실행 |
After Returning Advice | 대상 객체 메소드 익셉션 없이 실행된 이후->공통 기능 실행 |
After Throwing Advice | 대상 객체 메소드 실행 도중 익셉션 발생한 경우 -> 공통 기능 실행 |
After Advice | 익셉션 발생 여부와 상관X 대상 객체 메소드 실행 후 -> 공통 기능 실행 |
Around Advice*** | 대상 객체 메소드 실행 전/후 또는 익셉션 발생 시점 -> 공통 기능 실행 |
*** Around Advice 多 사용 – 다양한 시점에 원하는 기능 삽입 가능하므로
[스프링 AOP 구현]
<스프링 AOP를 이용해서 공통 기능 구현 -> 적용하는 방법>
➀ Aspect로 사용할 클래스에 @Aspect 붙임 ➁ @Pointcut 으로 공통 기능 적용 대상(Pointcut) 정의 ➂ 공통 기능 구현 메소드에 @Around 붙임 |
▶[@Aspect, @Pointcut, @Around 이용한 AOP 구현]
<Around Advice에서 사용할 Aspect 예시 코드>
[= 메소드 실행 전/후(Around)에 사용할 공통 기능(Aspect) 예시 코드 ]
@Aspect // Aspect로 사용할 클래스
public class ExeTimeAspect {
@Pointcut("execution(public * chap07..*(..))")
//chap 07 및 하위 패키지에 위치한 public 메소드들을 공통 기능 적용 대상 선정
private void publicTarget() {
}
@Around("publicTarget()") //publicTarget()에 정의된 Pointcut(대상)에 공통 기능 메소드 적용한다.
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 적용 클래스는 Advice와 Pointcut을 함께 제공한다. -@Pointcut : 공통 기능 적용 대상 설정 값 = execution 명시자 이용해서 설정 -@Around : Advice를 Around로 설정. 값에는 publicTarget() | 즉, publicTarget()이 정의한 Pointcut에 공통 기능 적용하겠다. |
<스프링 설정 클래스 작성>
-위에서 @Aspect 클래스를 공통 기능으로 적용하려면 설정클래스에 @EnableAspectJAutoProxy붙여야 함
-이 애노테이션 추가 시, 스프링은 @Aspect 붙은 빈 객체 찾아서 빈 객체에 설정되어 있는 @Pointcut 설정과 @Around 설정을 사용한다.
@Configuration
@EnableAspectJAutoProxy // 공통 기능 적용 위해 붙임
public class AppCtx {
@Bean
public ExeTimeAspect exeTimeAspect() {
return new ExeTimeAspect();
}
@Bean
public Calculator calculator() {
return new RecCalculator();
}
}
[ProceedingJoinPoint 인터페이스]
-@Around 붙여준 공통 기능 메소드의 파라미터는 ProceedingJoinPoint 타입이다.
-이 타입의 변수로 대상 객체를 받고 (인터페이스) 내부적으로 정의된 proceed() 메소드로 대상 객체의 핵심 메소드를 호출한다.
@Around("publicTarget()") public Object measure(ProceedingJoinPoint joinPoint) throws Throwable { |
Signature getSignature() | 호출되는 메소드에 대한 정보를 구한다 |
Object getTarget() | 대상 객체를 구한다 |
Object getArgs() | 파라미터 목록을 구한다 |
-이 인터페이스는 대상 객체에 대한 추가 정보에 접근할 수 있도록 다음 메소드를 추가적으로 정의해놓고 제공한다.
추가) + Signature 인터페이스는 다음 메소드를 제공. -String getName(): 호출 메소드 이름 -String toLongString():호출 메소드를 완전히 표현한 문장 (패키지/리턴타입/파라미터 타입 등) -String toShortString():호출 메소드 축약해서 표현한 문장 |
[프록시 생성 방식]
-스프링은 AOP를 위한 프록시 객체를 생성할 때 실제 생성할 빈 객체가 인터페이스를 상속하면
① 인터페이스를 이용해서 프록시를 생성함
② (인터페이스가 아닌) 클래스를 이용해서 프록시를 생성
@EnableAspectAoutoProxy(proxyTargetClass = true) -proxyTargetClass 속성을 true로 지정 시, 자바 클래스를 상속받아 프록시르 생성함 |
[excution 명시자 표현식]
-execution 명시자는 Advice 적용할 메소드 지정 시 사용.
ex. @Pointcut(“excution(public * chap07 ..*(..))”)
execution( 수식어패턴/리턴타입/클래스이름/메소드이름(파라미터) ) |
-스프링 AOP는 public 메소드에만 적용 O / 따라서 수식어 패턴 생략 가능
* 이용: 모든 값 표현 ..(점 2개) 이용 : 0개 이상을 표현 |
[Advice 적용 순서]
-한 개의 Pointcut에 여러 Advice를 적용할 수도 있다.
-어떤 @Aspect가 먼저 적용될 지는 자바 버전, 스프링 프레임워크에 따라 천차만별
Q. 적용 순서가 중요하다면?-> @Order 이용해서 직접 순서 지정해야 한다. @Order(값) : 값 작은 대상을 먼저 적용함 |
[@Around의 Pointcut 설정과 @Pointcut 재사용]
⓵ @Around에 (@Pointcut이 아닌) execution명시자를 직접 지정할 수도 O
⓶공통 Pointcut 재사용할 수 O
- 같은 Pointcut을 여러 Advice가 함께 사용 시 사용함
⓷별도의 클래스로 Pointcut 정의하여 사용 O
-이때, 별도의 클래스는 빈으로 꼭 등록할 필요는 X
-여러 Aspect에서 공통으로 사용하는 Pointcut이 있다면 별도 클래스에 Pointcut 정의 후,
각 Aspect가 해당 객체 생성하여 사용하도록 구성
'Web(웹)_관련 공부 모음 > [개념]_스프링 5 프로그래밍' 카테고리의 다른 글
ch09. 스프링 MVC 시작하기 (0) | 2022.01.31 |
---|---|
ch08. DB 연동 (0) | 2022.01.28 |
ch06. 빈 라이프사이클과 범위 (0) | 2022.01.27 |
ch05. 컴포넌트 스캔 (0) | 2022.01.26 |
ch04. 의존 자동 주입 (0) | 2022.01.26 |