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生態繁榮的一個重要原因