调用代理方法
分析調用邏輯之前先上類圖,看看Spring 中主要的AOP 組件:
上面我們已經了解到Spring 提供了兩種方式來生成代理方式有JDKProxy 和CGLib。下面我們來研究一下Spring 如何使用JDK 來生成代理對象,具體的生成代碼放在JdkDynamicAopProxy 這個類中,直接上相關代碼:
/*** 獲取代理類要實現的接口,除了Advised對象中配置的,還會加上SpringProxy, Advised(opaque=false)* 檢查上面得到的接口中有沒有定義 equals或者hashcode的接口* 調用Proxy.newProxyInstance創建代理對象*/ @Override public Object getProxy(@Nullable 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 是JDK 動態代理的核心,生成的代理對象的方法調用都會委托到InvocationHandler.invoke()方法。而從JdkDynamicAopProxy 的源碼我們可以看到這個類其實也實現了InvocationHandler,下面我們分析Spring AOP 是如何織入切面的,直接上源碼看invoke()方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Object target = null;try {//eqauls()方法,具目標對象未實現此方法if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}//hashCode()方法,具目標對象未實現此方法else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}else if (method.getDeclaringClass() == DecoratingProxy.class) {// There is only getDecoratedClass() declared -> dispatch to proxy config.return AopProxyUtils.ultimateTargetClass(this.advised);}//Advised接口或者其父接口中定義的方法,直接反射調用,不應用通知else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.//獲得目標對象的類target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// Get the interception chain for this method.//獲取可以應用到此方法上的Interceptor列表List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.//如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args)if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// We need to create a method invocation...//創建MethodInvocationinvocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}// Massage return value if necessary.Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method// is type-compatible. Note that we can't help if the target sets// a reference to itself in another returned object.retVal = proxy;}else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}} }主要實現思路可以簡述為:首先獲取應用到此方法上的通知鏈(Interceptor Chain)。如果有通知,則應用通知,并執行JoinPoint;如果沒有通知,則直接反射執行JoinPoint。而這里的關鍵是通知鏈是如何獲取的以及它又是如何執行的呢?現在來逐一分析。首先,從上面的代碼可以看到,通知鏈是通過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,我們來看下這個方法的實現邏輯:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {MethodCacheKey cacheKey = new MethodCacheKey(method);List<Object> cached = this.methodCache.get(cacheKey);if (cached == null) {cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);this.methodCache.put(cacheKey, cached);}return cached; }通過上面的源碼我們可以看到, 實際獲取通知的實現邏輯其實是由AdvisorChainFactory 的getInterceptorsAndDynamicInterceptionAdvice()方法來完成的,且獲取到的結果會被緩存。下面來分析getInterceptorsAndDynamicInterceptionAdvice()方法的實現:
/*** 從提供的配置實例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor,* 則判斷此Advisor能否應用到目標類targetClass上.如果是PointcutAdvisor,則判斷* 此Advisor能否應用到目標方法method上.將滿足條件的Advisor通過AdvisorAdaptor轉化成Interceptor列表返回.*/ @Override public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());//查看是否包含IntroductionAdvisorboolean hasIntroductions = hasMatchingIntroductions(config, actualClass);//這里實際上注冊一系列AdvisorAdapter,用于將Advisor轉化成MethodInterceptorAdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();for (Advisor advisor : config.getAdvisors()) {if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//這個地方這兩個方法的位置可以互換下//將Advisor轉化成InterceptorMethodInterceptor[] interceptors = registry.getInterceptors(advisor);//檢查當前advisor的pointcut是否可以匹配當前方法MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}else if (advisor instanceof IntroductionAdvisor) {IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}else {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return interceptorList; }這個方法執行完成后,Advised 中配置能夠應用到連接點(JoinPoint)或者目標類(Target Object)的Advisor 全部被轉化成了MethodInterceptor,接下來我們再看下得到的攔截器鏈是怎么起作用的。
// Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. //如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args) if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else {// We need to create a method invocation...//創建MethodInvocationinvocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed(); }從這段代碼可以看出, 如果得到的攔截器鏈為空, 則直接反射調用目標方法, 否則創建MethodInvocation,調用其proceed()方法,觸發攔截器鏈的執行,來看下具體代碼:
@Override @Nullable public Object proceed() throws Throwable {// We start with an index of -1 and increment early.//如果Interceptor執行完了,則執行joinPointif (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);//如果要動態匹配joinPointif (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;//動態匹配:運行時參數是否滿足匹配條件if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.//動態匹配失敗時,略過當前Intercetpor,調用下一個Interceptorreturn proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.//執行當前Intercetporreturn ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);} }至此,通知鏈就完美地形成了。我們再往下來看invokeJoinpointUsingReflection()方法,其實就是反射調用:
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)throws Throwable {// Use reflection to invoke the method.try {ReflectionUtils.makeAccessible(method);return method.invoke(target, args);}catch (InvocationTargetException ex) {// Invoked method threw a checked exception.// We must rethrow it. The client won't see the interceptor.throw ex.getTargetException();}catch (IllegalArgumentException ex) {throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +method + "] on target [" + target + "]", ex);}catch (IllegalAccessException ex) {throw new AopInvocationException("Could not access method [" + method + "]", ex);} }?
總結
 
                            
                        