Spring源碼分析-SpringAop什么時候調用jdk動態代理?什么時候調用cglib


 

1.導入log4j.jar,開啟log4j DEBUG模式

 

2.查看打印日志,可以發現一個重要信息:

2020-03-03 15:13:31,870 DEBUG [org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator] - Creating implicit proxy for bean 'userService' with 0 common interceptors and 4 specific interceptors

spring再執行aop時,執行了 AnnotationAwareAspectJAutoProxyCreator 類中的某個方法。

備注:有時候通過打印日志,可以快速了解代碼執行了哪些過程。

 
3.通過打印日志提示的類 AnnotationAwareAspectJAutoProxyCreator 及“Creating implicit proxy”找到最終執行的是其父類 AbstractAutoProxyCreator 中的 protected Advisor[]  buildAdvisors(String beanName, Object[] specificInterceptors) 方法

 

4. 重新在 buildAdvisors 這個方法打斷點進行調試

 

5.AbstractAutoProxyCreator 類中發現有一個重要的方法createProxy 如下:

protected Object createProxy(
    Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    // 核心代碼
    return proxyFactory.getProxy(getProxyClassLoader());
}

 

接着進入 ProxyFactory 中的 getProxy 方法

public Object getProxy(ClassLoader classLoader) {
    // createAopProxy 方法中判斷是選擇jdk動態代理還是cglib方式代理
    return createAopProxy().getProxy(classLoader);
}

 

再進入到 ProxyCreatorSupport 中的 createAopProxy 方法

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    // getAopProxyFactory() 返回的是 DefaultAopProxyFactory對象,所以重點還是在 createAopProxy 方法上
    return getAopProxyFactory().createAopProxy(this);
}

 

6.進入到 DefaultAopProxyFactory 類中的 createAopProxy 方法,這個方法就是如何選擇JDK動態代理還是Cglib代理

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 1.config.isProxyTargetClass() 代表 配置中的proxy-target-class屬性true/false,默認false
    // 
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        // 目標代理類,如 com.service.impl.UserServiceImpl
        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.");
        }
        // 目標類如果是一個接口 或者 (Proxy是JDK動態代理用到的一個類)也就是說這里表示目標類是否為這個Proxy 類型
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

 這段代碼是確定Spring選擇JDK動態代理還理CGLIB代理的核心,

  • config.isOptimize:表示是否使用了優化策略,配置的屬性optimize值決定;
  • config.isProxyTargetClass:表示是否是代理目標類,配置的屬性proxy-target-class值決定;
  • hasNoUserSuppliedProxyInterfaces:就是在判斷代理的對象是否有實現接口

 

總結: 

1、如果目標對象實現了接口,默認情況下會采用JDK的動態代理實現AOP
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現了接口,必須采用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

 

從這個邏輯也可以間接看出spring推薦使用JDK動態代理,因為高版本的JDK中,至少JDK1.6以上JDK動態代理的效率遠高於CGLIB(無論是單例模式還是多例模式)。但是如果代理的類沒有相應的接口,此時只能使用CGLIB代理了,例如代理模式應用在POJO實體類上,或者是說你的JDK版本較低,這時你可以手動設置CGLIB代理。

 

 方法中參數config 是一個 AdvisedSupport類型, AdvisedSupport 繼承了 ProxyConfig 類,

public class ProxyConfig implements Serializable {
    // 代表 配置中的proxy-target-class,如果設置true,則使用Cglib方式代理
    private boolean proxyTargetClass = false;
   // 是否優化
    private boolean optimize = false;

    boolean opaque = false;

    boolean exposeProxy = false;

    private boolean frozen = false;

    // 省略部分代碼
    
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM