Spring AOP --JDK動態代理方式

Jack2k發表於2021-09-09

我們知道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 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.
            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.
                //攔截器鏈是空的,直接執行需要代理的方法
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }            else {                // We need to create a method invocation...
                //這裡是呼叫攔截器鏈的地方,先建立一個MethodInvocation物件,然後呼叫該物件的proceed方法完成攔截器鏈呼叫
                invocation = 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.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);
            }
        }
    }

攔截器鏈的呼叫

從上面的程式碼和註釋中我們可以看到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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章