Spring AOP --JDK動態代理方式
我們知道Spring是透過JDK或者CGLib實現動態代理的,今天我們討論一下JDK實現動態代理的原理。
一、簡述
Spring在解析Bean的定義之後會將Bean的定義生成一個BeanDefinition物件並且由BeanDefinitionHolder物件持有。在這個過程中,如果Bean需要被通知切入,BeanDefinition會被重新轉換成一個proxyDefinition(其實也是一個BeanDefinition物件,只不過描述的是一個ProxyFactoryBean)。ProxyFactoryBean是一個實現了FactoryBean的介面,用來生成被被切入的物件。Spring AOP的實現基本上是透過ProxyFactoryBean實現的。我們今天討論的重點也是這個類。
在討論ProxyFactoryBean之前,我們先看一下一個BeanDefinition轉換成proxyDefintion的過程。
public final BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definitionHolder, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); // get the root bean name - will be the name of the generated proxy factory bean String existingBeanName = definitionHolder.getBeanName(); BeanDefinition targetDefinition = definitionHolder.getBeanDefinition(); BeanDefinitionHolder targetHolder = new BeanDefinitionHolder(targetDefinition, existingBeanName + ".TARGET"); // delegate to subclass for interceptor definition BeanDefinition interceptorDefinition = createInterceptorDefinition(node); // generate name and register the interceptor String interceptorName = existingBeanName + "." + getInterceptorNameSuffix(interceptorDefinition); BeanDefinitionReaderUtils.registerBeanDefinition( new BeanDefinitionHolder(interceptorDefinition, interceptorName), registry); BeanDefinitionHolder result = definitionHolder; if (!isProxyFactoryBeanDefinition(targetDefinition)) { // create the proxy definition 這裡建立proxyDefinition物件,並且從原來的BeanDefinition物件中複製屬性 RootBeanDefinition proxyDefinition = new RootBeanDefinition(); // create proxy factory bean definition proxyDefinition.setBeanClass(ProxyFactoryBean.class); proxyDefinition.setScope(targetDefinition.getScope()); proxyDefinition.setLazyInit(targetDefinition.isLazyInit()); // set the target proxyDefinition.setDecoratedDefinition(targetHolder); proxyDefinition.getPropertyValues().add("target", targetHolder); // create the interceptor names list proxyDefinition.getPropertyValues().add("interceptorNames", new ManagedList()); // copy autowire settings from original bean definition. proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate()); proxyDefinition.setPrimary(targetDefinition.isPrimary()); if (targetDefinition instanceof AbstractBeanDefinition) { proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition); } // wrap it in a BeanDefinitionHolder with bean name result = new BeanDefinitionHolder(proxyDefinition, existingBeanName); } addInterceptorNameToList(interceptorName, result.getBeanDefinition()); return result; }
二、ProxyFactoryBean的原理
我們先來看一下ProxyFactoryBean的繼承關係:
ProxyFactoryBean類圖
ProxyFactoryBean實現了FactoryBean、BeanClassLoaderAware、BeanFactoryAware介面,這裡就不多說了。ProxyCreatorSupport這個類則是建立代理物件的關鍵所在。 我們先來看看產生代理物件的方法:
public Object getObject() throws BeansException { initializeAdvisorChain(); if (isSingleton()) { //單例 return getSingletonInstance(); } else { if (this.targetName == null) { logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " + "Enable prototype proxies by setting the 'targetName' property."); } //非單例 return newPrototypeInstance(); } }
initializeAdvisorChain() 方法是將通知鏈例項化。然後判斷物件是否要生成單例而選擇呼叫不同的方法,這裡我們只看生成單例物件的方法。
private synchronized Object getSingletonInstance() { if (this.singletonInstance == null) { this.targetSource = freshTargetSource(); //如果以介面的方式代理物件 if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) { // Rely on AOP infrastructure to tell us what interfaces to proxy. Class> targetClass = getTargetClass(); if (targetClass == null) { throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy"); } //獲取目標類實現的所有介面,並註冊給父類的interfaces屬性,為jdk動態代理做準備 setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader)); } // Initialize the shared singleton instance. super.setFrozen(this.freezeProxy); //這裡產生代理物件 this.singletonInstance = getProxy(createAopProxy()); } return this.singletonInstance; }
我們可以看到,產生代理物件是透過getProxy()方法實現的,這個方法我們看一下:
protected Object getProxy(AopProxy aopProxy) { return aopProxy.getProxy(this.proxyClassLoader); }
AopProxy物件的getProxy()方法產生我們需要的代理物件,究竟AopProxy這個類是什麼,我們接下來先看一下產生這個物件的方法createAopProxy():
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); } createAopProxy方法: public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //目標物件不是介面類的實現或者沒有提供代理介面 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //代理物件自身是介面 if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
在這裡我們只看JdkDynamicAopProxy這個類的實現,我們前面提到,真正代理物件的生成是由AopProxy的getProxy方法完成的,這裡我們看一下JdkDynamicAopProxy的getProxy方法,這也是本文討論的重點:
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); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
我們看可以很清楚的看到,代理物件的生成直接使用了jdk動態代理:Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
而代理邏輯是透過實現了InvocationHandler介面的invoke方法實現的。而這裡用到的實現了InvocationHandler介面的類就是JdkDynamicAopProxy自身。JdkDynamicAopProxy自身實現了InvocationHandler介面,完成了Spring AOP攔截器鏈攔截等一系列邏輯,我們看一下JdkDynamicAopProxy的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; Class> targetClass = null; Object target = null; try { //沒有重寫equals方法 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } //沒有重寫hashCode方法 if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } //代理的類是Advised,這裡直接執行,不做任何代理 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; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. //獲得代理物件 target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. //獲得已經定義的攔截器鏈 List
攔截器鏈的呼叫
從上面的程式碼和註釋中我們可以看到spring實現aop的主要流程,具體如何呼叫攔截器鏈,我們來看一下MethodInvocation的proceed方法
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // currentInterceptorIndex是從-1開始的,所以攔截器鏈呼叫結束的時候index是 this.interceptorsAndDynamicMethodMatchers.size() - 1 // 呼叫鏈結束後執行目標方法 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 獲得當前處理到的攔截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // 這裡判斷是否是InterceptorAndDynamicMethodMatcher,如果是,這要判斷是否匹配methodMatcher,不匹配則此攔截器不生效 if (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. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
proceed()方法是一個遞迴方法,我們可以根據程式碼的註釋知道大體邏輯,InterceptorAndDynamicMethodMatcher的程式碼如下,我們可以看到,InterceptorAndDynamicMethodMatcher 持有一個MethodInterceptor 物件和一個MethodMatcher 物件,在攔截器鏈呼叫過程中,如果攔截器是InterceptorAndDynamicMethodMatcher ,則會先根據MethodMatcher 判斷是否匹配,匹配MethodInterceptor 才會生效。
class InterceptorAndDynamicMethodMatcher { final MethodInterceptor interceptor; final MethodMatcher methodMatcher; public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) { this.interceptor = interceptor; this.methodMatcher = methodMatcher; } }
至於MethodInterceptor 是什麼,MethodInterceptor 的邏輯是怎麼樣的,我們可以看一下MethodInterceptor 的一個子類AfterReturningAdviceInterceptor的實現:
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { private final AfterReturningAdvice advice; /** * Create a new AfterReturningAdviceInterceptor for the given advice. * @param advice the AfterReturningAdvice to wrap */ public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }}
AfterReturningAdviceInterceptor的作用是在被代理的方法返回結果之後新增我們需要的處理邏輯,其實現方式我們可以看到,先呼叫MethodInvocation 的proceed,也就是先繼續處理攔截器鏈,等呼叫完成後執行我們需要的邏輯:this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
到這裡,spring使用jdk動態代理實現aop的分析基本上結束,其中攔截器鏈的呼叫比較難懂而且比較重要,需要的同學可以多看看這一塊。
在程式設計師這條路上遇到瓶頸的朋友可以加入群:654675708 大家一起來提升進步 但要備註好資訊 注! 有Java高階大牛直播講解知識點,分享知識,有五大專題都是各位老師多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知
階段一:工程化技術-提升效率 才能有更多的時間來思考
階段二:原始碼分析-成為一個內功深厚的程式設計師
階段三:高效能 分散式 高可用-進入網際網路公司不再是你的難題
階段四:效能調優-我不甘心只做一個程式設計師 我還有更高的成就
階段五:專案實戰-理論與時間實踐相結合 你離夢想的距離只學要你點起腳尖
作者:西西老師
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/36/viewspace-2802249/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- 死磕Spring之AOP篇 - 初識JDK、CGLIB兩種動態代理SpringJDKCGLib
- AOP - 自己寫 JDK 動態代理增強 beanJDKBean
- 從動態代理到Spring AOP(中)Spring
- 從動態代理到Spring AOP(上)Spring
- 【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件SpringJDKCGLib物件
- JDK動態代理JDK
- Java動態代理(AOP)Java
- Java-JDK動態代理(AOP)使用及實現原理分析JavaJDK
- 動態代理jdk的Proxy與spring的CGlibJDKSpringCGLib
- AOP之靜態代理VS動態代理
- Spring AOP裡的靜態代理和動態代理,你真的瞭解嘛?Spring
- Spring動態代理的生成-如何判斷是使用JDK動態代理還是CGlib代理SpringJDKCGLib
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- Spring AOP 自動建立代理Spring
- JDK動態代理初探JDK
- JDK動態代理和 CGLIB 代理JDKCGLib
- JDK動態代理和CGLib代理JDKCGLib
- 死磕Spring之AOP篇 - Spring AOP自動代理(三)建立代理物件Spring物件
- 淺析Spring中AOP的實現原理——動態代理Spring
- 【Spring】AOP的代理預設是Jdk還是Cglib?SpringJDKCGLib
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- Jmh測試JDK,CGLIB,JAVASSIST動態代理方式的效能JDKCGLibJava
- JDK動態代理詳解JDK
- 深挖JDK動態代理(一)JDK
- 死磕Spring之AOP篇 - Spring AOP自動代理(一)入口Spring
- 淺析DispatchProxy動態代理AOP
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- 深入理解靜態代理與JDK動態代理JDK
- Spring 動態代理Spring
- spring動態代理Spring
- Spring的JDK動態代理如何實現的(原始碼解析)SpringJDK原始碼
- spring上 -基於註解配置bean,動態代理,AOP筆記SpringBean筆記
- Java動態代理(JDK和cglib)JavaJDKCGLib
- 你必須會的 JDK 動態代理和 CGLIB 動態代理JDKCGLib
- Spring框架系列(12) - Spring AOP實現原理詳解之JDK代理實現Spring框架JDK
- Spring原始碼剖析5:JDK和cglib動態代理原理詳解Spring原始碼JDKCGLib
- 【乾貨】JDK動態代理的實現原理以及如何手寫一個JDK動態代理JDK