本文完整代碼地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybeanjava
FactoryBean
和BeanFactory
因爲在命名上極其類似,一直以來困擾了很多的開發者。git
BeanFactory
,耳熟能詳的Spring核心接口,提供IoC容器的最基本功能。但要解釋FactoryBean
一句話可能就說不清楚了。咱們將從下面的例子逐步說明,FactoryBean是什麼,它提供了什麼樣的能力。程序員
/** * 布料 * 包含顏色屬性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Red red; //省略setter/getter方法 }
當初始化一個Cloth對象時,我但願Red對象也被賦值,此時我將在Cloth的構造方法中new一個Red對象。github
public Cloth() { red = new Red(); }
可是隨着業務的發展,我但願Cloth的顏色屬性將是Blue藍色,這時我將修改代碼將Red和Blue類抽象出一個Color接口,Cloth代碼將重構:spring
/** * 布料 * 包含顏色屬性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { color = new Blue(); } //省略setter/getter方法 }
業務又進一步發展,Cloth類中的顏色屬性將會根據必定的條件賦值爲Red紅色,此時咱們將代碼繼續重構:測試
/** * 布料 * 包含顏色屬性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { if (condition()) { color = new Blue(); } else { color = new Red(); } } //省略setter/getter方法 }
這樣的代碼的確能運行,但若是有新的條件繼續加入到業務中,此時咱們又將改動Cloth類的構造方法,而咱們認爲Cloth方法是一個比較核心的業務對象,不該該常常對它進行修改,而且在構造方法中對於Color對象建立過於冗餘,不符合單一職責的原則,因此咱們將Color對象的建立過程經過工廠方法模式來完成。代理
咱們再次將Cloth類進行以下重構(爲了使示例代碼更加簡潔,下面的示例將只建立Red對象):日誌
/** * 布料 * 包含顏色屬性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { color = StaticColorFactory.newInstance(); } //省略setter/getter方法 }
/** * 靜態工廠方法 * Created by OKevin On 2019/9/3 **/ public class StaticColorFactory { public static Color getInstance() { return new Red(); } }
若是咱們在Spring容器中要經過靜態工廠方法,建立具體的對象實例應該怎麼作呢?code
衆所周知,要將一個對象實例交由Spring容器管理,咱們一般是經過如下XML配置:xml
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.bean.Red" />
但此時,Red對象實例並非由Spring容器管理,而是由靜態工廠方法建立的,此時咱們應該講XML配置修改成如下方式:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.factory.StaticColorFactory" factory-method="getInstance" />
這是Spring支持靜態工廠方法建立對象實例的特定方式。這樣咱們就能在Spring中經過靜態工廠方法建立對象實例。
有靜態工廠方法,就有非靜態工廠方法,區別就是方法不是靜態的。
/** * 實例工廠方法 * Created by OKevin On 2019/9/3 **/ public class ColorFactory { public Color getInstance() { return new Red(); } }
實例工廠方法在Spring中XML配置略有不一樣:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="colorFactory" class="com.coderbuff.factory.ColorFactory"/> <bean id="red" factory-bean="colorFactory" factory-method="getInstance"/>
經過配置能夠看到,咱們須要首先在Spring中實例化工廠,再經過工廠對象實例化Red對象。
在有了對工廠方法在Spring中建立對象實例的認識後,FactoryBean實際上就是爲咱們簡化這個操做。下面咱們將經過FactoryBean來建立Red對象。
/** * Created by OKevin On 2019/9/3 **/ public class ColorFactoryBean implements FactoryBean<Color> { public Color getObject() throws Exception { return new Red(); } public Class<?> getObjectType() { return Red.class; } public boolean isSingleton() { return false; } }
經過實現FactoryBean的方式,XML配置以下:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.factory.ColorFactoryBean"/>
這樣就不用像工廠方法那樣配置相應的屬性,直接按照普通的Bean注入便可,因爲Spring內部作了特殊處理,此時名稱爲「red」的Bean並非ColorFactoryBean,而是它方法中getObject中返回的對象。若是實在想要獲取ColorFactoryBean的對象實例,則在Bean的名稱前加入「&」便可(「&red」)。
看到這裏,是否對FactoryBean有了一點認識呢?FactoryBean在Spring中最爲典型的一個應用就是用來建立AOP的代理對象。
咱們知道AOP其實是Spring在運行時建立了一個代理對象,也就是說這個對象,是咱們在運行時建立的,而不是一開始就定義好的,這很符合工廠方法模式。更形象地說,AOP代理對象經過Java的反射機制,在運行時建立了一個代理對象,在代理對象的目標方法中根據業務要求織入了相應的方法。這個對象在Spring中就是——ProxyFactoryBean。
咱們將經過比較「古老」的方式建立一個Red對象的切面,在它的print方法執行前和執行後分別執行一條語句。之因此古總是由於咱們每每經過註解的方式,而不會這麼折磨本身去寫一個切面對象。
/** * 環繞通知 * Created by OKevin On 2019/9/4 **/ public class LogAround implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("調用目標方法【前】打印日誌"); Object result = invocation.proceed(); System.out.println("調用目標方法【後】打印日誌"); return result; } }
此時咱們須要ProxyFactoryBean的介入爲咱們建立一個代理對象並由Spring容器管理,根據上面ColorFactoryBean的經驗,ProxyFacoryBean也應該以下配置:
<bean id="xxx" class="org.springframework.aop.framework.ProxyFactoryBean" />
答案是確定的,只是ProxyFactoryBean多了幾個參數,既然是生成代理對象,那麼目標對象、目標方法就必不可少,實際的XLM配置以下:
<bean id="logAround" class="com.coderbuff.aop.LogAround"/> <bean id="red" class="com.coderbuff.bean.Red"/> <bean id="proxyRed" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.coderbuff.bean.Color"/> <property name="interceptorNames" value="logAround"/> <property name="target" ref="red"/> <property name="proxyTargetClass" value="true"/> </bean>
經過測試程序,ProxyFactoryBean的確生成了一個代理對象。
public class ProxyFactoryBeanTest { private ClassPathXmlApplicationContext ctx; @Before public void init() { ctx = new ClassPathXmlApplicationContext("spring-proxyfactorybean.xml"); } @Test public void testProxyFactory() { Red proxyRed = (Red) ctx.getBean("proxyRed"); proxyRed.print(); } }
因此,FactoryBean爲咱們實例化Bean提供了一個更爲靈活的方式,咱們能夠經過FactoryBean建立出更爲複雜的Bean實例。
本文完整代碼地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybean