學習AOP之透過Spring的Ioc理解Advisor


花了幾天時間來學習Spring,突然明白一個問題,就是看書不能讓人理解Spring,一方面要結合使用場景,另一方面要閱讀源代碼,這種方式理解起來事半功倍。那看書有什么用呢?主要還是擴展視野,畢竟書是別人總結出來的東西,看一遍可以發現自己的理解偏差,而且還可以看到一些平時不太關注的內容,當然看也可以是一種學習技術的方式。

最開始只是想了解一下AOP,沒想到就陷的這么深,為了搞清楚spring是如何完成切面功能這兩天還是把Ioc部分的內容也給讀了讀。還是看懂了大概,只不過這復雜的內部結構確實不易理解與閱讀,我在想Spring確實是個好的開源軟件,但代碼可能真的少了點親近感。一個BeanFactory和FactroyBean就可以寫上好幾頁紙來說明,畢竟這些名字沒有多少Ioc的影子。

一、Spring Ioc的簡單理解

對於Ioc的功能就不再多說,這里主要是理解一下Ioc的關鍵代碼,至於BeanFactory、ApplicationContent、Resource之類就不說了,直接從getBean開始吧。

從最基本的容器開始:

public interface ISay {
	void say();
	void noaop();
}

public class SayImpl implements ISay{

	public void say() {
		System.out.print("我是5207.");
	}

	public void noaop() {
		System.out.println("別aop我");
	}

}


public class Client {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
		ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
		say1.say();
		say1.noaop();
	}
}

這里面有一個sayImpl的java代碼,下面是spring.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="sayImpl" class="aop.demo.SayImpl"></bean>
</beans>

運行Client的會得到下面的結果:

我是5207.別aop我

這里的XmlBeanFactory只是用來解析Xml文件而創建的類,它本身只是傳遞了一個Resource而已,最終的容器功能是在AbstractBeanFactory中完成,那么我們直接就看AbstractBeanFactory中getBean的實現,而getBean又調用了doGetBean方法,這才是本尊。

下面是doGetBean的主要代碼,代碼太多,有些不是很重要的就刪除了。

protected <T> T doGetBean(
		final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
		throws BeansException {

	final String beanName = transformedBeanName(name);
	Object bean;

	//先從緩存里查找單例的對象
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
	    //如果對象存在則判斷是否要通過FactoryBean來審批
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
	else {
		//判斷是否需要從父ParentBeanFactory獲得對象
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
			// Not found -> check parent.
			String nameToLookup = originalBeanName(name);
			if (args != null) {
				// Delegation to parent with explicit args.
				return (T) parentBeanFactory.getBean(nameToLookup, args);
			}
			else {
				// No args -> delegate to standard getBean method.
				return parentBeanFactory.getBean(nameToLookup, requiredType);
			}
		}

		//先把當前bean依賴的bean給加載起來,通過getBean遞歸來完成
		String[] dependsOn = mbd.getDependsOn();
		if (dependsOn != null) {
			for (String dependsOnBean : dependsOn) {
				getBean(dependsOnBean);
				registerDependentBean(dependsOnBean, beanName);
			}
		}

		if (mbd.isSingleton()) {
		    //創建單實例的對象
			sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
				public Object getObject() throws BeansException {
					try {
					    //這里是真正的實例化一個對象的地方
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						destroySingleton(beanName);
						throw ex;
					}
				}
			});
			//看看是不是FactoryBean,如果是的話要使用FactoryBean.getObject來返回對象
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		else if (mbd.isPrototype()) {
			// 如果是prototype,創建新對象
			Object prototypeInstance = null;
			try {
				beforePrototypeCreation(beanName);
				prototypeInstance = createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
			bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		}

		else {
			String scopeName = mbd.getScope();
			final Scope scope = this.scopes.get(scopeName);
			if (scope == null) {
				throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
			}
			try {
				Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
					public Object getObject() throws BeansException {
						beforePrototypeCreation(beanName);
						try {
							return createBean(beanName, mbd, args);
						}
						finally {
							afterPrototypeCreation(beanName);
						}
					}
				});
				bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
			}
			catch (IllegalStateException ex) {
				throw new BeanCreationException(beanName,
						"Scope '" + scopeName + "' is not active for the current thread; " +
						"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
						ex);
			}
		}
	}

	return (T) bean;
}

其實上面的代碼中可以找出一段代碼中完成核心調用的是createBean和getObjectForBeanInstance這兩個方法。getObjectForBeanInstance的主要邏輯是

  • 檢查對象是否為FactoryBean類型,如果不是直接返回對象實例
  • 如果是FactoryBean類型,那么就要通過FactoryBean的getObject來返回對象實例

看到這里就明白了FactoryBean的用處了吧,這個也是Spring Aop與Ioc進行交互的一個重要邏輯。

再來看看createBean

會發現createBean在AbstractBeanFactory中只是一個虛方法,說明是在派生類中實現的,前面是從XmlBeanFactory開始的,那么順着它的繼承關系看看,最終是在AbstractAutowireCapableBeanFactory中找到了createBean的實現:

@Override
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
		throws BeanCreationException {
....省略.
	Object beanInstance = doCreateBean(beanName, mbd, args);
....省略.
	return beanInstance;
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
	Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

....省略.
	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		if (exposedObject != null) {
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
	}
	catch (Throwable ex) {
....省略.
	}
....省略.
	return exposedObject;
}

代碼太多了,找關鍵部分吧。createBean的過程主要是

  • 生成bean的wrapper

首先是創建好bean,然后將新創建的bean實例並放在BeanWrapper中返回。

  • populateBean的調用

1、先是執行InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation,這里會有實例創建后的一個回調,如果是false會退出整個過程。

2、完成對象的注入autowireByName/autowireByType,這里就看到很眼熟的東西了吧“autowire”。

3、調用InstantiationAwareBeanPostProcessor的postProcessPropertyValues

4、最后是applyPropertyValues方法,把屬性值都寫入

  • initializeBean的調用

1、先是invokeAwareMethods,完成BeanNameAware、BeanClassLoaderAware、BeanFactoryAware的注入

2、然后是BeanPostProcessor的postProcessBeforeInitialization調用

3、完成InitializingBean的調用

4、然后是BeanPostProcessor的postProcessAfterInitialization調用,這里就是后面Aop中Advistor接管對象的地方啦

可以看到initializeBean方法最后返回的是wrappedBean有兩種可能,一種是經過BeanPostProcessor生成的對象,如果當前bean沒有注冊BeanPostProcessor的話就直接返回bean自己。aop中就運用這個方法來完成代碼的切面編程。

小結
寫到這Ioc創建一個對象的過程就說完了,過程中發現了很多Ioc容器對一個對象生成過程的技巧與奧秘,也理解了為什么可以在spring中靈活的完成對對象創建的各種“干預”。

二、Advisor與Ioc的聯系

好了,接下來開始進入spring aop部分,前面的例子做一些補充加入aop的功能,spring.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 聲明被代理的目標對象 -->
    <bean id="sayImpl" class="aop.demo.SayImpl"></bean>
    <!-- 聲明用於增強的攔截器對象 -->
    <bean id="sayImplAroundAdvice" class="aop.demo.SayImplAroundAdvice"></bean>

    <!-- 配置一個切面 -->
    <bean id="sayAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="sayImplAroundAdvice"/>            <!-- 增強 -->
        <property name="pattern" value="aop.demo.SayImpl.s.*"/> <!-- 切點(正則表達式) -->
    </bean>   
   
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <property name="optimize" value="true"/>
    </bean>
</beans>

這里有一個環繞增強及一個切面,最后使用DefaultAdvisorAutoProxyCreator來完成自動代理。前面在學習Ioc時使用的是XmlBeanFactory,是一個基本的基於xml的ioc容器,那么我們再使用ClassPathXmlApplicationContext吧,看看會發生什么變化:

public class Client {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
		ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
		say1.say();
		say1.noaop();

		ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
		ISay say = (ISay)context.getBean("sayImpl");
		say.say();
		say.noaop();
	}
}

執行結果:

我是5207.別aop我
大家好:我是5207.希望大家多多點贊.
別aop我

可以發現使用ClassPathXmlApplicationContext容器時aop的功能才有用,有點奇怪啊,都是讀取的配置為啥結果不同呢?這里主要是BeanFactory和ApplicationContext的主要區別之一。查看XmlBeanFactory的加載過程發現其只是調用了一個XmlBeanDefinitionReader讀取了spring.xml並解析為BeanDefinition,然后具體的bean都要到getBean時才會去實例化,而且bean的依賴也要自己去處理。

那applicationContext呢?從ClassPathXmlApplicationContext的代碼發現就復雜一些,在構造函數中有一個調用比較特別:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
		throws BeansException {

	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}

看到這里發現多了一個refresh()的過程,進去一看一堆的處理。其實主要過程就是初始化Ioc容器,完成容器的配置,加載好並注冊好各種回調。比如前面提到的BeanPostProcessor就是在這個refresh里就完成了注冊。

正因為refresh這個過程我們用起spring來才會覺得簡單好用。

先從DefaultAdvisorAutoProxyCreator說起

有了上面的基礎再來看DefaultAdvisorAutoProxyCreator就會簡單的多。我們可以看一下DefaultAdvisorAutoProxyCreator的類繼承關系,其中有一個AbstractAutoProxyCreator需要特別關注。這個AbstractAutoProxyCreator實現了一堆的接口:

public abstract class AbstractAutoProxyCreator extends ProxyConfig
		implements SmartInstantiationAwareBeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
		Ordered, AopInfrastructureBean {

其中SmartInstantiationAwareBeanPostProcessor繼承關系中最底層的是BeanPostProcessor,前面說明Ioc的時候提到過,BeanPostProcessor可以在getBean時返回BeanPostProcessor加工過的對象。好了,奧秘就在這里啦,看一下AbstractAutoProxyCreator中如何實現的吧:

public Object postProcessBeforeInitialization(Object bean, String beanName) {
	return bean;
}

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.containsKey(cacheKey)) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

其中Before方法啥也沒做,都在After中完成的。在After中有一個wrapIfNecessary方法,這個方法的作用是如果需要進行代理則生成代理並返回代理對象實例。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
		return bean;
	}
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

方法中使用了一個緩存advisedBeans存取bean是否需要advise,如果是需要的話就會存TRUE,否則就是FALSE,這樣下次再來取時不用再判斷直接返回結果就行。當然最重要的是如果是需要為bean生成代理時則會創建代理啦。至於如何判斷是否需要代理是通過一個getAdvicesAndAdvisorsForBean方法來完成的,如果getAdvicesAndAdvisorsForBean返回的是DO_NOT_PROXY則表示不需要代理。為了擴展getAdvicesAndAdvisorsForBean是抽象方法。從DefaultAdvisorAutoProxyCreator的父類AbstractAdvisorAutoProxyCreator中找到了getAdvicesAndAdvisorsForBean的實現:

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
	List advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
		return DO_NOT_PROXY;
	}
	return advisors.toArray();
}

這里面會查找所有的Advisors並返回,當然這個過程中因為Advisor是包含了Advice的,所以Aop的基本要素就到齊了。然后回到wrapIfNecessary中,接着就會調用createProxy來生成代理對象啦。

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

	ProxyFactory proxyFactory = new ProxyFactory();
	// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
	proxyFactory.copyFrom(this);

	if (!shouldProxyTargetClass(beanClass, beanName)) {
		// Must allow for introductions; can't just set interfaces to
		// the target's interfaces only.
		Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
		for (Class<?> targetInterface : targetInterfaces) {
			proxyFactory.addInterface(targetInterface);
		}
	}

	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	for (Advisor advisor : advisors) {
		proxyFactory.addAdvisor(advisor);
	}

	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}

	return proxyFactory.getProxy(this.proxyClassLoader);
}

createProxy過程主要是通過ProxyFactory來生成代理對象,在之前的《學習AOP之深入一點Spring Aop》里已經講過。最后將生成的代理對象放入緩存並返回給Ioc容器。

從些之后,getBean時獲得的已經不是beanName的實際對象,而是代理對象。


免責聲明!

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



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