面試真題--------spring源碼解析AOP


接着上一章對IOC的理解之后,再看看AOP的底層是如何工作的。

1.實現AOP的過程

   首先我們要明白,Spring中實現AOP,就是生成一個代理,然后在使用的時候調用代理。

          1.1 創建代理工廠

              代碼中首先創建一個代理工廠實例ProxyFactory proxyFactory = new ProxyFactory();代理工廠的作用就是使用編程的方式創建AOP代理。ProxyFactory繼承自AdvisedSupport,AdvicedSupport是AOP代理的配置管理器。然后是設置要代理的目標對                 象proxyFactory.setTarget(new LoginServiceImpl());,看下setTarget方法:

public void setTarget(Object target) {
//先根據給定的目標實現類,創建一個單例的TargetSource
//然后設置TargetSource
setTargetSource(new SingletonTargetSource(target));
}

         1.2  添加通知                      

上面設置了要代理的目標類之后,接着是添加通知,也就是添加增強類,proxyFactory.addAdvice()方法是添加增強類的方法。我們在例子中是這么使用的:

1
2
3
proxyFactory.addAdvice(new LogBeforeLogin());//前置增強
proxyFactory.addAdvice(new LogAfterLogin());//后置增強
//proxyFactory.addAdvice(new LogAroundLogin());//環繞增強

addAdvice方法的參數是一個Advice類型的類,也就是通知或者叫增強,可以去我們的增強類中查看,我們都繼承了各種Advice,比如MethodBeforeAdviceAfterReturningAdviceMethodInterceptor。

        1.3 獲取代理

         

JDK動態代理方式獲取代理

JDK動態代理方式獲取代理,實現在JdkDynamicAopProxy中:

1
2
3
public Object getProxy() {
return getProxy(Thread.currentThread().getContextClassLoader());
}
1
2
3
4
5
6
7
public Object getProxy(ClassLoader cl) {
//JDK動態代理只能代理接口類型,先獲取接口
//就是從AdvisedSupport中獲取保存在interfaces中的接口
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advisedSupport);
//使用Java的反射機制創建一個代理實例
return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}

關於JDK反射創建代理之類的,這里不做解析。

CGLIB方式獲取代理

CGLIB獲取方式,實現在Cglib2AopProxy中:

1
2
3
4
public Object getProxy() {
//使用CGLIB的方式來獲取,CGLIB這里不做解析
return getProxy(Thread.currentThread().getContextClassLoader());
}

使用代理

上面獲取代理之后,就剩最后一步,使用,當我們調用業務方法的時候,實際上是調用代理中的方法,對於CGLIB生成的代理,調用的是DynamicAdvisedInterceptor的intercept方法;JDK動態代理生成的代理是調用invoke方法。

調用ProxyFactoryBean的getObject方法,也就是對bean增強的地方。下面我們着重來看一下是如何對bean進行增強的。首先我們進入到ProxyFactoryBean的getObject方法來看一下。

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();
        }
    }
復制代碼

此處主要是先初始化了一下通知器鏈,然后就會根據是否單例做相應的動作,我們看一下初始化通知器鏈的進行。

復制代碼
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
        if (this.advisorChainInitialized) {
            return;
        }

        if (!ObjectUtils.isEmpty(this.interceptorNames)) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                        "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
            }

            // Globals can't be last unless we specified a targetSource using the property...
            if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                    this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                throw new AopConfigException("Target required after globals");
            }

            // Materialize interceptor chain from bean names.
            for (String name : this.interceptorNames) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Configuring advisor or advice '" + name + "'");
                }

                if (name.endsWith(GLOBAL_SUFFIX)) {
                    if (!(this.beanFactory instanceof ListableBeanFactory)) {
                        throw new AopConfigException(
                                "Can only use global advisors or interceptors with a ListableBeanFactory");
                    }
                    addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                            name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                }

                else {
                    // If we get here, we need to add a named interceptor.
                    // We must check if it's a singleton or prototype.
                    Object advice;
                    if (this.singleton || this.beanFactory.isSingleton(name)) {
                        // Add the real Advisor/Advice to the chain.
                        advice = this.beanFactory.getBean(name);
                    }
                    else {
                        // It's a prototype Advice or Advisor: replace with a prototype.
                        // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                        advice = new PrototypePlaceholderAdvisor(name);
                    }
                    addAdvisorOnChainCreation(advice, name);
                }
            }
        }

        this.advisorChainInitialized = true;
    }
復制代碼

可以看到,其中針對我們配置的interpretorNames進行了循環,我們並非是配置的全局通知器,所以會進入else塊,然后因為我們配置的testAdvisor默認是單例的,所以會從bean工廠中去獲取這個實例,此時TestAdvisor已經實例化完成的,我們只是去取一下而已。然后就會進入addAdvisorOnChainCreation方法,就是把通知器加到了通知鏈當中。

              值得注意的是在這個過程中,觸發了一個這樣的方法this.advisorAdapterRegistry.wrap(next)。這個方法就是用來包裝通知器的,如果不是advisor而是advice,就會包裝一下返回。

              好了,接着剛才的過程,初始化通知器鏈完成以后,就會進入getSingletonInstance方法,這是用來獲取單例實例的,而真正的加強也是在這里發生的,我們來看一下。

復制代碼
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");
                }
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }
復制代碼

此時第一次獲取,單例實例為null,所以會進入if塊,首先刷新targetSource,因為我們的Target類沒有實現targetSource接口,所以會由spring幫我們產生一個targetSource適配,這里是使用的適配器的模式,有興趣可以進去看一下,我們此處不關注這個。接下來,會去判斷代理接口,並且設置代理接口,但是我們的target未實現任何接口,所以此處interfaces仍然為空的,所以最后一步createAopProxy時,會幫我們創建cglib的proxy。最終由cglib生成代理返回。

 

2.spring aop通知(advice)分成五類

前置通知[Before advice]:在連接點前面執行,前置通知不會影響連接點的執行,除非此處拋出異常。 
正常返回通知[After returning advice]:在連接點正常執行完成后執行,如果連接點拋出異常,則不會執行。 
異常返回通知[After throwing advice]:在連接點拋出異常后執行。 
返回通知[After (finally) advice]:在連接點執行完成后執行,不管是正常執行完成,還是拋出異常,都會執行返回通知中的內容。 
環繞通知[Around advice]:環繞通知圍繞在連接點前后,比如一個方法調用的前后。這是最強大的通知類型,能在方法調用前后自定義一些操作。環繞通知還需要負責決定是繼續處理join point(調用ProceedingJoinPoint的proceed方法)還是中斷執行。 

 

總結一下,主要說幾點:

1.在IOC容器初始化的過程中,並沒有發生增強的動作,而是初始化了proxyFactoryBean。

2.如果配置中不指定,所有bean默認都是單例和非延遲加載的,也就是說所有的bean都將在第一次IOC容器初始化時全部實例化,所以上一章中所配置的三個bean都是在IOC容器初始化時進行的實例化。

3.springAOP代理有兩種方式,一種是JDK提供的動態代理,一種是cglib字節碼生成的技術,當要代理的類有實現的接口的時候,就會針對接口進行代理,否則就會采用cglib直接生成字節碼產生子類。

 

 

 

參考---http://www.cnblogs.com/zuoxiaolong/p/spring7.html

           http://cxis.me/2017/04/12/Spring%E4%B8%ADAOP%E6%BA%90%E7%A0%81%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90/


免責聲明!

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



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