1. 程式人生 > >Spring原始碼-AOP(五)-ProxyFactoryBean

Spring原始碼-AOP(五)-ProxyFactoryBean

上一章中我們分析了ProxyFactory,它是Spring AOP核心的底層實現。然而硬編碼的方式還是過於繁瑣且不易使用,本章我們將討論ProxyFactoryBean,它結合了ProxyFactory和Ioc中的FactoryBean擴充套件,使得可以通過XML配置的方式來實現Spring AOP。關於Spring AOP建立代理的具體實現本章將不會再贅述,而是主要討論FactoryBean擴充套件結合Spring AOP的使用。

1.FactoryBean簡介

我在IOC容器(四)-FactoryBean一篇中已詳細地介紹了FactoryBean在Spring IOC中的實現原理,這裡還是簡單介紹一下。

FactoryBean顧名思義,工廠Bean,即可以動態建立Bean的Bean。對於一些只有在執行時才能明確的物件來說,直接在XML中定義已不能滿足,而是通過定義一個工廠類,在執行時根據不同配置來生成最終的物件。

FactoryBean是Spring IOC的兩大擴充套件之一(另一個是BeanPostProcessor),由此與Spring整合的功能眾多,包括但不限於AOP,ORM,事務管理,Remoting。

簡單看下AbstractBeanFactory中對FactoryBean處理的程式碼

// 單例
if (mbd.isSingleton()) {
	// 獲取單例的bean物件
	sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
		[@Override](https://my.oschina.net/u/1162528)
		public Object getObject() throws BeansException {
			try {
				// 匿名內部類中建立bean物件
				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process, to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		}
	});
	// 處理FactoryBean擴充套件
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

可以看到,物件建立完成後會判斷是否為FactoryBean的子類,如果是,則會呼叫getObject方法返回真正的物件。

2.ProxyFactoryBean實現

回到ProxyFactoryBean,先貼出之前例子的配置。

<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<!-- 原始物件 -->
	<bean id="chromeBrowser" class="com.lcifn.spring.aop.bean.ChromeBrowser"/>
	<!-- 環繞增強物件 -->
	<bean id="browserAroundAdvice" class="com.lcifn.spring.aop.advice.BrowserAroundAdvice"></bean>
	
	<bean id="browserProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 介面 -->
		<property name="interfaces" value="com.lcifn.spring.aop.bean.Browser"/>
		<!-- 要代理的物件 -->
		<property name="target" ref="chromeBrowser"/>
		<!-- 攔截器組 -->
		<property name="interceptorNames">
			<list>
				<value>browserAroundAdvice</value>
			</list>
		</property>
	</bean>
</beans>

基本配置就不說了,按照上面FactoryBean的介紹,Spring建立完ProxyFactoryBean物件後,就會呼叫其getObject方法。

public Object getObject() throws BeansException {
	// 初始化Advisor鏈
	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();
	}
}

getObject方法就做了兩件事,一是初始化Advisor鏈,二是建立代理物件,不過建立代理物件時區分單例和多例。

初始化Advisor鏈

簡單地說,就是將配置中的interceptorNames轉化成Advisor物件。其中又分為名稱精確匹配和名稱全域性匹配兩種,精確匹配就不用說了,全域性匹配就是以*號結尾的這種。

對於全域性匹配這種,就是在BeanFactory中匹配所有Advisor和Interceptor型別的Bean,再用*前的名稱字首去匹配Bean的名稱。

private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
	String[] globalAdvisorNames =
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
	String[] globalInterceptorNames =
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
	List<Object> beans = new ArrayList<Object>(globalAdvisorNames.length + globalInterceptorNames.length);
	Map<Object, String> names = new HashMap<Object, String>(beans.size());
	for (String name : globalAdvisorNames) {
		Object bean = beanFactory.getBean(name);
		beans.add(bean);
		names.put(bean, name);
	}
	for (String name : globalInterceptorNames) {
		Object bean = beanFactory.getBean(name);
		beans.add(bean);
		names.put(bean, name);
	}
	OrderComparator.sort(beans);
	for (Object bean : beans) {
		String name = names.get(bean);
		if (name.startsWith(prefix)) {
			addAdvisorOnChainCreation(bean, name);
		}
	}
}

而對於精確匹配的更加清晰,直接取BeanFactory中找,而如果ProxyFactoryBean的singleton屬性設定為false時,則封裝為PrototypePlaceholderAdvisor延遲建立。

// 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);

兩種方式最後都通過addAdvisorOnChainCreation來執行新增操作。其中重要的是將根據name從BeanFactory中獲得的物件轉換成Advisor,因為interceptorNames支援Advisor,Advice,MethodInterceptor多種型別的物件name,通過namedBeanToAdvisor方法統一轉成Advisor型別。

private void addAdvisorOnChainCreation(Object next, String name) {
	// We need to convert to an Advisor if necessary so that our source reference
	// matches what we find from superclass interceptors.
	// 轉換Bean物件為Advisor
	Advisor advisor = namedBeanToAdvisor(next);
	if (logger.isTraceEnabled()) {
		logger.trace("Adding advisor with name '" + name + "'");
	}
	addAdvisor(advisor);
}

private Advisor namedBeanToAdvisor(Object next) {
	try {
		return this.advisorAdapterRegistry.wrap(next);
	}
	catch (UnknownAdviceTypeException ex) {
		// We expected this to be an Advisor or Advice,
		// but it wasn't. This is a configuration error.
		throw new AopConfigException("Unknown advisor type " + next.getClass() +
				"; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
				"which may also be target or TargetSource", ex);
	}
}

這裡的advisorAdapterRegistry實現為DefaultAdvisorAdapterRegistry,這個wrap方法也是被多個地方呼叫以達到統一Advisor,之後方便生成代理攔截器鏈。

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
	if (adviceObject instanceof Advisor) {
		return (Advisor) adviceObject;
	}
	if (!(adviceObject instanceof Advice)) {
		throw new UnknownAdviceTypeException(adviceObject);
	}
	Advice advice = (Advice) adviceObject;
	if (advice instanceof MethodInterceptor) {
		// So well-known it doesn't even need an adapter.
		return new DefaultPointcutAdvisor(advice);
	}
	for (AdvisorAdapter adapter : this.adapters) {
		// Check that it is supported.
		if (adapter.supportsAdvice(advice)) {
			return new DefaultPointcutAdvisor(advice);
		}
	}
	throw new UnknownAdviceTypeException(advice);
}

建立代理物件

建立代理物件時根據singleton屬性決定建立的物件是單例還是多例。我們以單例的來看

private synchronized Object getSingletonInstance() {
	if (this.singletonInstance == null) {
		// 返回targetSource
		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;
}

freshTargetSource判斷targetName為null,直接返回targetSource,否則從beanFactory根據targetName獲取target物件。

對於代理介面集合為空,且proxyTargetClass為空時,從targetClass獲取其實現的介面集合作為代理介面。因而在配置ProxyFactoryBean時,可以不用指定interfaces屬性。

getProxy方法的實現同ProxyFactory一致,都是通過AopProxyFactory->AopProxy->Proxy的方式,具體可見ProxyFactory中的建立代理一節。返回的物件賦予類變數作為單例快取。

對於多例的情況,呼叫的newPrototypeInstance方法

private synchronized Object newPrototypeInstance() {

	ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory());
	// The copy needs a fresh advisor chain, and a fresh TargetSource.
	TargetSource targetSource = freshTargetSource();
	copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());
	if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
		// Rely on AOP infrastructure to tell us what interfaces to proxy.
		copy.setInterfaces(
				ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
	}
	copy.setFrozen(this.freezeProxy);

	if (logger.isTraceEnabled()) {
		logger.trace("Using ProxyCreatorSupport copy: " + copy);
	}
	return getProxy(copy.createAopProxy());
}

通過建立一個新的基類物件ProxyCreatorSupport,並copy所有配置的方式每次生成一個新的物件,最後通過getProxy方法獲取代理物件。

至此ProxyFactoryBean的介紹就結束了,它主要的特點就是使用FactoryBean同IOC進行了結合。大家都知道Spring框架是一種微核心的設計,包括其AOP,事務管理等許多功能都是通過擴充套件的方式來設計的。而這種設計思想隨著深入到各功能原始碼,包括一些其他框架同Spring的整合,不能不說這是Spring生態繁榮的一個重要原因