본문 바로가기

동방프로젝트

7과-2

@Configuration
@EnableAspectJAutoProxy
public class AppCtxWithCache {

	@Bean
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}

	
	@Bean
	public CacheAspect cacheAspect() {
		return new CacheAspect();
	}

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

CacheAspect: Cache에 추가[7]
RecCalculator.factorial([7]) 실행시간 : 446500 ns
CacheAspect: Cache에서 구함[7]
RecCalculator.factorial([7]) 실행시간 : 118600 ns
CacheAspect: Cache에 추가[5]
RecCalculator.factorial([5]) 실행시간 : 99200 ns
CacheAspect: Cache에서 구함[5]
RecCalculator.factorial([5]) 실행시간 : 90300 ns

 

@Bean
	public CacheAspect cacheAspect() {
		return new CacheAspect();
	}
	@Bean
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}

	


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

RecCalculator.factorial([7]) 실행시간 : 35800 ns
CacheAspect: Cache에 추가[7]
CacheAspect: Cache에서 구함[7]
RecCalculator.factorial([5]) 실행시간 : 12400 ns
CacheAspect: Cache에 추가[5]
CacheAspect: Cache에서 구함[5]

 

--> 순서에 따라, 실행이 달라짐

 

factorial(7) 실행은 같은데, 첫 번째는 ExeTimeAspect와 CacheAspect가 다 적용, 후자는 CacheAspect만 적용됨
 * 이는, Advice를 "CacheAspect 프록시 - ExeTimeAspect 프록시 - 실제 대상객체" 순으로 적용했기 때문
 * 14행에서 구한 calculator빈은 사실 CacheAspect프록시 객체. 그런데 CacheAspect프록시 객체의 대상 객체는
 * ExeTimeASpect의 프록시 객체. 마지막으로 ExeTimeAspect프록시의 대상객체가 실제 대상객체(AppCtx의 순서에 따라 대상객체가 변경)

 

따라서, CacheAspect의 Object result = joinPoint.proceed();를 만나면,

그 대상객체인 ExeTimeAspect의 measure로 넘어간다. 여기서 또 Object result = joinPoint.preceed()를 만나면,

그 대상객체인 Calculator의 factorial로 넘어간다. 

다음으로, ExeTimeAspect의 finally를 실행

다음으로, CacheAspect의 put부터 실행

 

만약 순서가 다르다면, exe-cache-cal순으로 넘어가므로, 실행-추가-구함이 아니라, 추가-실행-구함-실행 이 되는것

 

근데 이건 개멍청하다. 객체지향에서 순서에 따라 바뀐다니?? 

따라서, @Order을 통해 그 순서를 지정해줄 필요가 있다.

@Bean
	@Order(1)
	public CacheAspect cacheAspect() {
		return new CacheAspect();
	}
	
	@Bean
	@Order(2)
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}

 

 

//cacheAspect_1
@Aspect //aspect가 있는 클래스는 공통으로 쓰일 것들
public class CacheAspect {

	private Map<Long, Object> cache = new HashMap<>();
	
	@Pointcut("execution(public * chap07..*(long))")
	public void cacheTarget() {
		
	}
	
	@Around("cacheTarget()")
	public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
		Long num = (Long)joinPoint.getArgs()[0];	//첫번째 인자를 long타입으로 구함
		if(cache.containsKey(num)) {	//위에서 구한 키값이 cache에 존재하면, 키에 해당하는 값을 구해 리턴
			System.out.printf("CacheAspect: Cache에서 구함[%d]\n", num);
			return cache.get(num);
		}
		
		Object result = joinPoint.proceed(); //long num 키값이 cache에 부재하면, 프록시 대상 객체를 실행
		cache.put(num,  result); //프록시 대상 객체의 결과(result)를 cache에 추가
		System.out.printf("CacheAspect: Cache에 추가[%d]\n", num);
		return result; //프록시 대상 객체의 실행결과를 리턴
	}
}

/*
@Around값은 cacheTarget. @Pointcut 설정은 첫 번쨰 인자가 long인 메서드를 대상으로 한다. 따라서 execute()메서드는
Calculator의 factorial(long)메서드에 적용된다.
새 Aspect를 구현했으니, 스프링 설정 클래스에서 두 개의 Aspect 추가가 가능. 두 Aspect에서 설정한 pointcut은 모두 
Calculator타입의 factorial()메서드에 적용됨
*/

pointcut이 아닌, around 애노테이션에 execution명시자를 직접지정

 

public class ExeTimeAspect {
	@Pointcut("execution(public * chap07..*(..))") //Aspect 애노테이션 적용클래스는 advice와 pointcut 제공
	//pointcut은 공통기능 적용할 대상을 설정. chap07패키지와 그 하위 패키지의 public 메서드를 pointcut으로 설정
		private void publicTarget() {
			
		}
	/*
	 * public void publicTarget()을 사용시, 다른 클래스에 위치한 @Around애노테이션에서 publicTarget()메서드의 
	 * Pointcut을 사용할 수 있다. 
	 * 그리고 해당 Pointcut의 완전한 클래스 이름을 포함한 매서드 이름을 @Around 애노테이션에 사용하면 된다.
	 * 예) @Aspect
	 * public class CacheAspect {
	 * 	@Around("aspect.ExeTimeAspect.publicTarget()"0
	 * 	public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
	 * 	...}
	 * }

단, CacheAspect와 ExeTimeAspect는 같은 패키지 내 위치하므로, 패키지 이름(aspect)는 굳이 빼도 됨.

 

여러 Aspect 내에서 공통으로 쓰는 Pointcut이 있다면, 별도 클래스에 Pointcut을 정의하고, 각 Aspect 클래스에서 해당 Pointcut을 쓰도록 하면 관리가 용이.

public class CommonPointcut {
	@Pointcut("execution(public * chap07..*(..))")
    public void commonTarget() {
    }
}

///

@Aspect
public class CacheAspect {
	private Map<Long, Object> cache = new HashMap<>();
	
    @Around("CommonPointcut.commonTarget()")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
		Long num = (Long)joinPoint.getArgs()[0];
        ......
        
///

@Aspect 
public class ExeTimeAspect {
	
	@Around("CommonPointcut.commonTarget()") 
	public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
    }
    ...

 

'동방프로젝트' 카테고리의 다른 글

spring의 transactional&proxy  (2) 2021.01.25
8과 queryForObject()  (2) 2021.01.23
7과  (2) 2021.01.20
@Qualifier 어노테이션  (2) 2021.01.15
스프링 chap02  (2) 2021.01.13