스프링부트 해부학 : AOP(1) - ProxyFactory

정윤성·2022년 6월 7일
1

스프링부트 해부학

목록 보기
10/20

역할

출처 : https://linked2ev.github.io/gitlog/2019/09/22/springboot-mvc-14-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-MVC-Spring-AOP-%EC%84%A4%EC%A0%95/

AOP란 정말 간단하다 로직의 Template즉 비즈니스 로직부분을 뺀 나머지 부분에 대한 프로그래밍 방식이다

널리 알고있는 마크업 언어로 표현을 해보자

<트랜잭션>
<로그>
비즈니스 로직
</로그>
</트랜잭션>

다음과 같은 코드가 있을 때 <트랜잭션> <로그> 부분을 걷어내고 비즈니스 로직만 실제 코드에 노출시키는 방식이다

정리하면 트랜잭션, 로그와 같은 종단적인 문제가아닌 횡단 공통사항들에 대해 묶는걸 AOP라고 한다

ProxyFactory

Spring에서는 Interface기반의 JdkDynamicAopProxy와 CGLIB기반의 CglibAopProxy를 제공한다

public interface AopProxy {
	Object getProxy();
}

AopProxy는 getProxy를 통해 위의 getProxy에 접근할 수 있다


ProxyFactory는 위의 getProxy를 이용해 우리에게 Proxy객체를 제공해준다

ProxyCreatorSupport가 Proxy객체를 생성하고 AdvisedSupport를 통해 실제 ProxyChain을 받아온다

이에대해서 순차적으로 살펴보자


먼저 Proxy생성부분이다

public class ProxyFactory extends ProxyCreatorSupport {
	...
	public static Object getProxy(TargetSource targetSource) {
    	ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTargetSource(targetSource);
		proxyFactory.setProxyTargetClass(true);
		return proxyFactory.getProxy();
    }
  	...
    public Object getProxy() {
		return createAopProxy().getProxy();
	}
    ...
}

public class ProxyCreatorSupport extends AdvisedSupport {
	...
    protected final synchronized AopProxy createAopProxy() {
    	...
		return getAopProxyFactory().createAopProxy(this);
	}
    ...
}

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    	if(..) {
        	if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
            return new ObjenesisCglibAopProxy(config);
        }
        else {
			return new JdkDynamicAopProxy(config);
		}
    }
}

위와같이 AopProxyFactory를 통해 위 2가지 Type중 상황에맞게 Proxy객체를 받아온다 각 getProxy방법에 대해서 좀더 알아보자

JdkDynamicAopProxy

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	...
    
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
    	...
    	return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
    }
    
    @Override
	@Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	...
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        ...
        MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        retVal = invocation.proceed();
        ...
    }
    ...
}

JdkDynamicAopProxy는 Proxy.newProxyInstance로 생성한다
Jdk는 위와같이 ProxyChain을 받아와 MethodInvocation구현체로 실행한다

CglibAopProxy

class CglibAopProxy implements AopProxy, Serializable {
	
    ...

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
    	...
        Enhancer enhancer = createEnhancer();
        ...
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        ...
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    
    ...
}

class ObjenesisCglibAopProxy extends CglibAopProxy {

	private static final SpringObjenesis objenesis = new SpringObjenesis();
    ...

 	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    	...
        proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
        ...
        return proxyInstance;
	}
    ...
}

CglibAopProxy같은 경우는 Enhancer객체를 만들어서 이를 통해 Proxy를 구현하는데 기존에는 super클래스에 생성자호출, 본인클래스에 생성자호출 기본생성자 구현 등의 문제가 있었지만 Objensis라이브러리를 이용해 위의 단점들을 극복했다

Cglib같은경우 정말 복잡한데 얘 또한

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

해당 chain을 불러와 MethodInterceptor 구현체를 통해 처리한다

위에 두가지 방식다 정말 많은 방식의 Proxy처리기술이 있지만 대표적인것만 알아봤다

ProxyChain

이제 ProxyChain생성방법에 대해서 보자

public class AdvisedSupport extends ProxyConfig implements Advised {
	
    ...
    
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    	...
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
        	this, method, targetClass);
        ...
        return cached;
    }
    
    @Override
	public final Advisor[] getAdvisors() {
		return this.advisorArray;
	}
}

위 메서드를 통해 ProxyChain을 가져온다

AdvisedSupport는 Advisor를 List형태로 들고있으며 이를 getInterceptorsAndDynamicInterceptionAdvice메서드를 통해 Chain으로 만들게된다

해당 메서드에 대해 확인해보면

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
	
    @Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    	Advised config, Method method, @Nullable Class<?> targetClass) {
        ...
        Advisor[] advisors = config.getAdvisors();
    	List<Object> interceptorList = new ArrayList<>(advisors.length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        for (Advisor advisor : advisors) {
        	if (advisor instanceof PointcutAdvisor) {
            	PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if(.. || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                	...
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    if(..) {
                        ...
                    }
                    else {
                        match = mm.matches(method, actualClass);
                    }
                    if (match) {
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        ...
                        interceptorList.addAll(Arrays.asList(interceptors));
                        ...
                	}
                    ...
                }
                ...
            }
            ...
        }
        return interceptorList;
    }
}

등록되어있는 Advisor를 Pointcut을 비교한 뒤 해당하는 부분들에 대해서 MethodInterceptor를 추가한다 그리고 이를 반환하는데 이것이 ProxyChain이다

최종적으로는 JdkProxy나 Cglib이 Chain을 순회하면서 invoke하는 방식이다

여담 : 해당 MethodInterceptor는 아래와 같다
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
	Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}

정리

  1. Spring에서 제공하는 2가지 Proxy방식에 대해서 알 수 있었다
  2. ProxyFactory에서 어떻게 Proxy, ProxyChain을 생성해는지 알 수 있었다
profile
게으른 개발자

0개의 댓글