티스토리 뷰
프록시 생성방식
스프링은 프록시를 생성할 때, 실제 생성할 빈 객체가 인터페이스를 상속하면 프록시 타입 또한 해당 인터페이스를 상속해서 만들어지게 됩니다. 즉 다음과 같은 코드는 오류가 발생합니다.
	//in Configuration code..
	@Bean
	public Calculator calculator() {
		return new REcCalculator();
	}package main;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import chap07.Calculator;
import chap07.REcCalculator;
import config.AppCtx;
public class MainAspect {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext(AppCtx.class);
		
		//Calculator cal = ctx.getBean("calculator",Calculator.class);
		REcCalculator cal= ctx.getBean("calculator",REcCalculator.class);
		cal.factorial(5);
		System.out.println(cal.getClass().getName());
	}
}
실제 빈 객체의 타입 RECcalculator은 Calculator 인터페이스를 상속했으므로 프록시 객체를 생성할 때 Calculator을 상속해서 만들어지게 됩니다. 그러므로 RECcalculator 타입으로 캐스팅이 불가능합니다.
만약 인터페이스가 아닌 클래스를 상속해 프록시 객체를 만들고 싶다면 proxyTargetClass의 속성을 true로 바꿔주면 됩니다.
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import apsect.ExeTimeAspect;
import chap07.Calculator;
import chap07.REcCalculator;
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppCtx {
	@Bean
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}
	
	@Bean
	public Calculator calculator() {
		return new REcCalculator();
	}
}
Execution 명시자
execution 명시자의 기본형태는 다음과 같습니다.
execution([수식어패턴] 리턴패턴 [클래스패턴]메서드패턴(파라미터패턴))
대괄호안에 있는 패턴들은 생략이 가능합니다. 유의할점은 클래스패턴을 명시할 때, 패키지명까지 모두 적어야 한다는 것입니다. 아래는 execution 명시자의 예시입니다. * 패턴은 모든 값에 해당하며 .. 패턴은 0개 이상을 의미합니다.
| execution(public void set*(..)) | 리턴타입이 void이고, 메서드명이 set으로 시작하며 메서드의 인자가 0개 이상인 메서드들을 지정합니다 | 
| execution(* chap07.*.*()) | 클래스패턴을 명시하여 패키지명까지 다 적은 것입니다. chap07 패키지내에 모든 클래스의 메서드들을 지정합니다. | 
| execution(* chap07..*.*()) | chap07 패키지 및 하위 패키지를 포함한 모든 메서드를 지정합니다. | 
| execution(Long chap07.Calculator.factorial(..)) | 리턴 타입이 Long이고 Calculator 클래스의 factorial 메서드를 지정합니다. | 
| execution(* get*(*)) | 메서드명이 get으로 시작하고 인자를 하나만 갖는 모든 메서드를 지정합니다. | 
| execution(* get*(*,*)) | 메서드명이 get으로 시작하고 인자를 두개만 갖는 모든 메서드를 지정합니다. | 
| execution(* read*(Integer,..)) | 메서드명이 read로 시작하고 첫번째 인자가 Integer인 하나 이상의 인자를 갖는 메서드를 지정합니다. | 
Advice 적용 순서
하나의 Pointcut에 여러 Advice를 적용할 수 있습니다. 아래와 같은 새로운 Aspect를 만들어봅시다.
package apsect;
import java.util.HashMap;
import java.util.Map;
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 CacheAspect {
	private Map<Long,Object> cache = new HashMap<>();
	
	@Pointcut("execution(* chap07..*.*(..))")
	public void cacheTarget() {
		
	}
	
	@Around("cacheTarget()")
	public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
		Long num=(Long)joinPoint.getArgs()[0];
		if(cache.containsKey(num)) {
			System.out.println("이미 존재합니다");
			return cache.get(num);
		}
		Object ret = joinPoint.proceed();
		cache.put(num, ret);
		System.out.println("cache에 추가");
		return ret;
	}
}
설정 클래스에 추가한 Aspect의 Bean을 만든 후 Main에서 다음과 같이 실행해보도록 합시다.
package main;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import chap07.Calculator;
import chap07.REcCalculator;
import config.AppCtx;
public class MainAspect {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext(AppCtx.class);
		
		Calculator cal= ctx.getBean("calculator",Calculator.class);
		cal.factorial(5);
		cal.factorial(5);
		cal.factorial(7);
		cal.factorial(7);
		System.out.println(cal.getClass().getName());
	}
}

위와 같은 실행결과가 나타난 이유는 cacheApsect의 프록시 객체는 대상 객체로써 ExeTimeAspect 프록시 객체를 사용하고 ExeTimeAspect 프록시 객체는 대상 객체로써 실제 대상 객체를 사용하기 때문입니다.

만일 Aspect의 적용순서가 중요하다면 @Order 어노테이션을 통해 Aspect의 우선순위를 지정할 수 있습니다. 위 사진에서는 cacheAspect에 @Order(1) 을 지정하고 ExeTimeAspect에 @Order(2)를 지정한 것과 동일합니다.
'Books > 스프링5 프로그래밍 입문' 카테고리의 다른 글
| [Spring] @Transactional을 통한 트랜잭션 처리 (0) | 2021.03.22 | 
|---|---|
| [Spring] 스프링 MVC 시작하기 (0) | 2021.03.20 | 
| [Spring] AOP (1) (0) | 2021.03.13 | 
| [Spring] Proxy 객체 (0) | 2021.03.12 | 
| [Spring] 빈의 라이프 사이클 (0) | 2021.03.10 | 
- Total
- Today
- Yesterday
- dfs
- sweeping
- sorting
- Oracle
- spring boot
- kmp
- greedy
- 펜윅트리
- hld
- dijkstra
- DP
- Segment tree
- union find
- bfs
- SCC
- knapsack
- 이분탐색
- 이분매칭
- implementation
- 세그먼트트리
- 2-SAT
- 트라이
- 정렬
- Fenwick
- 좌표압축
- 스위핑
- 동적계획법
- spring
- string
- Suffix Array
| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | 
| 12 | 13 | 14 | 15 | 16 | 17 | 18 | 
| 19 | 20 | 21 | 22 | 23 | 24 | 25 | 
| 26 | 27 | 28 | 29 | 30 | 31 | 
