스프링부트 해부학 : AOP(2) - @EnableAspectJAutoProxy

정윤성·2022년 6월 9일
2

스프링부트 해부학

목록 보기
11/20

역할

ProxyFactory를 한번더 추상화한 Layer로 Asepect를 Annotation을 이용해 쉽게 AOP를 등록할 수 있다

@EnableAspectJAutoProxy같은경우 자동으로 추가되게 설정되어져있다

@EnableAspectJAutoProxy

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;

false = Interface기반 리플렉션 AOP
true = Class기반 CGLIB AOP

Import를 따라가보면

AspectJAutoProxyRegistrar.class

@Override
public void registerBeanDefinitions(
	AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
	...
}

AopConfigUtils.class

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
	BeanDefinitionRegistry registry, @Nullable Object source) {
    
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

registerAspectJAnnotationAutoProxyCreatorIfNecessary이부분을 통해

위사진과 같이 Asepect에 필요한 클래스들을 불러온다

AnnotationAwareAspectJAutoProxyCreator

우리가 @Aspect 어노테이션을 통해 Advice를 등록할 수 있게 도와주는 클래스이다

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
	...
	@Override
	protected List<Advisor> findCandidateAdvisors() {
    	List<Advisor> advisors = super.findCandidateAdvisors();
        if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
    }
    ...
}

findCandidateAdvisors를 통해 모든 후보 Advice를 찾아온다
이때 buildAspectJAdvisors를 통해 Advice를 선발한다

BeanFactoryAspectJAdvisorsBuilder.java

public List<Advisor> buildAspectJAdvisors() {
	...
    List<Advisor> advisors = new ArrayList<>();
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    	this.beanFactory, Object.class, true, false);
    for (String beanName : beanNames) {
    	if (!isEligibleBean(beanName)) {
        	continue;
        }
        ...
        if (this.advisorFactory.isAspect(beanType)) {
        	if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
            	...
                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                ...
                if (this.beanFactory.isSingleton(beanName)) {
                	this.advisorsCache.put(beanName, classAdvisors);
                }
                else {
                	this.aspectFactoryCache.put(beanName, factory);
                }
                ...
            	advisors.addAll(classAdvisors);
                ...
            }
        }
        ...
    }
    
   return advisors;
}

현재 등록되어있는 모든 Asepect로부터 BeanName에 해당하는 Aspect를 뿌려주는 부분이다

여기서 getAdvisors부분만 추가로 살펴보자

ReflectiveAspectJAdvisorFactory

먼저 해당 클래스의 상위클래스인 AbstractAspectJAdvisorFactory에 대해 간략히 짚고 넘어가자

AbstractAspectJAdvisorFactory.class

@Override
public void validate(Class<?> aspectClass) throws AopConfigException {
	Class<?> superclass = aspectClass.getSuperclass();
    if (superclass.getAnnotation(Aspect.class) != null &&
    !Modifier.isAbstract(superclass.getModifiers())) {
    	...
    }      
    ...
}

@Nullable
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
	A result = AnnotationUtils.findAnnotation(method, toLookFor);
    if (result != null) {
    	return new AspectJAnnotation<>(result);
    }
	else {
		return null;
	}
}

protected static class AspectJAnnotation<A extends Annotation> {
	static {
    	annotationTypeMap.put(Pointcut.class, AspectJAnnotationType.AtPointcut);
        annotationTypeMap.put(Around.class, AspectJAnnotationType.AtAround);
        annotationTypeMap.put(Before.class, AspectJAnnotationType.AtBefore);
        annotationTypeMap.put(After.class, AspectJAnnotationType.AtAfter);
        annotationTypeMap.put(AfterReturning.class, AspectJAnnotationType.AtAfterReturning);
        annotationTypeMap.put(AfterThrowing.class, AspectJAnnotationType.AtAfterThrowing);
    }
}
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
	Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    ...
    validate(aspectClass);
    ...
}

Aspect Annotation이 존재하는지 확인하는 validate메서드와 현재 메서서드에 붙어있는 Annotation정보를 가져온다

본론으로 돌아와서

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
	Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 대상클래스
    ...
    validate(aspectClass); // 검증
    
    ...
    List<Advisor> advisors = new ArrayList<>();
    for (Method method : getAdvisorMethods(aspectClass)) { // 메서드 리스트
    	Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); // Advisor 식별
        if (advisor != null) {
        	advisors.add(advisor); // 등록
		}
    }
    ...
    return advisors;
}

위와같이 대상클래스를 가져오고 식별한뒤 Aspect라면 메서드를 불러와서 하나하나 Advisor인지 구분하고 맞다면 이를 등록하는 과정을 거친다

validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());

getAdvisor를 통해 메서드에 대한 검증 및 Pointcut을 등록한다

AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);

Pointcut을 찾아올 때 상위 클래스에서 정의한 findAnnotation을 사용한다

이렇게 Adivsor객체를 생성하고 등록시킨다 또한 매번 생성시킬 순 없으니 캐시에 등록하는 모습도 볼 수 있었다

AbstractAutoProxyCreator

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ...
    // 위에서 확인한 findCandidateAdvisors를 내부적으로 호출
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    ...
    Object proxy = createProxy(
    	bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    ...
    return proxy;
}

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	...
    return wrapIfNecessary(bean, beanName, cacheKey);
    ...
}

AbstractAutoProxyCreator또한 BestPostProcessor이므로 After작업중 wrapIfNecessary의 getAdvicesAndAdvisorsForBean를 통해 findCandidateAdvisors를 호출해 사용가능한 Advisors객체들을 가져온다

findCandidateAdvisors를통해 모든 Advisors가 불러와졌고

최종반환은 실제 PointCut에 해당하는 Advisors만 specificInterceptors로 등록된다

참고 : 정확히는 SmartInstantiationAwareBeanPostProcessor해당 인터페이스를 상속하므로 Bean의 생성작업이 다 끝난뒤에 호출이 된다

정리

  1. Spring의 기본전략에 의해 AOP는 자동으로 켜져있다
  2. 저번 포스팅에서의 ProxyFactory를 한번더 추상화 시켜서 Spring에서는 @Aspect로 제공한다
  3. Proxy또한 기본적으로 BeanPostProcessor이므로 After때 Bean을 가로채서 Aspect Proxy객체를 반환한다
profile
게으른 개발자

0개의 댓글