接着上一章對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 |
proxyFactory.addAdvice(new LogBeforeLogin());//前置增強 |
addAdvice方法的參數是一個Advice類型的類,也就是通知或者叫增強,可以去我們的增強類中查看,我們都繼承了各種Advice,比如MethodBeforeAdvice
,AfterReturningAdvice
,MethodInterceptor。
1.3 獲取代理
JDK動態代理方式獲取代理
JDK動態代理方式獲取代理,實現在JdkDynamicAopProxy中:
1 |
public Object getProxy() { |
1 |
public Object getProxy(ClassLoader cl) { |
關於JDK反射創建代理之類的,這里不做解析。
CGLIB方式獲取代理
CGLIB獲取方式,實現在Cglib2AopProxy中:
1 |
public Object getProxy() { |
使用代理
上面獲取代理之后,就剩最后一步,使用,當我們調用業務方法的時候,實際上是調用代理中的方法,對於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/