proxy实例化
1. 根据是否有interface准备proxy:JdkDynamicAopProxy.getProxy()/CglibAopProxy.getProxy()
proxy方法调用
chain.isEmpty()==true: 直接调用target的对应method
chain.isEmpty()==false: 调用advice的invoke(MethodInvocation invocation) 在方法中可以通过invocation.proceed();继续调用chain的对应的下一个advice直到target。
1. 根据是否有interface准备proxy:JdkDynamicAopProxy.getProxy()/CglibAopProxy.getProxy()
JDK:
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
其中InvocationHandler就是JdkDynamicAopProxy本身
CGLIB:
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
return (this.constructorArgs != null ?
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
enhancer.create());
}
其中Callback[] 是CglibAopProxy静态内部类,有多个
proxy方法调用
2. CGLIB调用MethodInterceptor.intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
JDK调用InvocationHandler.invoke(Object proxy, Method method, Object[] args)
Step1: 得到当前method对应的advice调用链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Step2:
chain.isEmpty()==true: 直接调用target的对应method
chain.isEmpty()==false: 调用advice的invoke(MethodInvocation invocation) 在方法中可以通过invocation.proceed();继续调用chain的对应的下一个advice直到target。
Note:
1.
Spring的AOP实现可能是对JDK动态代理的妥协,让CGLIB的callback模拟JDK的InvocationHandler持有当前的target。正是因为Spring实现中最终都是调用的target的method,因此Spring的proxy方法调用自己的其他方法时不会触发AOP。
2.
有个有意思的地方在被CGLIB动态代理过的类再被Spring的AOP代理一次就会变成用JDK代理,因为CGLIB动态代理默认会继承一些接口。
正因为如此,这里会有个尴尬的地方,如果一个bean本身没有用接口,但是又被会被代理两次以上(第一次CGLIB,第二次JDK),那用@Autoware的地方就会抛错类似于:
Bean named 'xxxxxxx' is expected to be of type 'xxxxxxxx' but was actually of type 'com.sun.proxy.$Proxy21'
这时候解决办法有两个:
一:给bean加个接口
二:强制使用CGLIB : proxyTargetClass="true"