AOP란 정말 간단하다 로직의 Template즉 비즈니스 로직부분을 뺀 나머지 부분에 대한 프로그래밍 방식이다
널리 알고있는 마크업 언어로 표현을 해보자
<트랜잭션>
<로그>
비즈니스 로직
</로그>
</트랜잭션>
다음과 같은 코드가 있을 때 <트랜잭션> <로그> 부분을 걷어내고 비즈니스 로직만 실제 코드에 노출시키는 방식이다
정리하면 트랜잭션, 로그와 같은 종단적인 문제가아닌 횡단 공통사항들에 대해 묶는걸 AOP라고 한다
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방법에 대해서 좀더 알아보자
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구현체로 실행한다
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생성방법에 대해서 보자
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하는 방식이다
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}