本文繼續以前的2篇文章(BeanPostProcessor和BeanFactoryPostProcessor)介紹Ioc容器的功能擴展。java
FactoryBean是用來構造Bean的接口。常規狀況下向容器添加一個Bean只須要像下面這樣經過XML的配置或註解直接引入這個類便可:git
<bean id="a" class="x.y.z.A"> <property name="setter" value="1" /> </bean>
@Component class A{}
可是某些狀況下咱們須要動態的裝載一個復的Bean,此時可使用FactoryBean來動態裝載一個Bean。FactoryBean字面上看就知道它是一個Bean,可是有Factory的功能(工廠模式)。spring
FactoryBean的使用和以前介紹的Processor 同樣,實現一個接口,而後設置爲一個Spring的Bean便可:bash
class MyFactory implements FactoryBean{ @Override public Object getObject() throws Exception { return null; } @Override public Class getObjectType() { return null; } }
下面經過一個適配器的例子來講明FactoryBean的使用,文中的代碼僅用於示例,可執行源碼請移步 https://gitee.com/chkui-com/spring-core-sample 中的 chkui.springcore.example.xml.factorybean包。ide
例子是使用適配器模式對對應的資源進行解碼,執行一下3步:post
例子的代碼結構以下:ui
factorybean --BeanFactoryApp.java main方法 --AdapterFactory.java 一個FactoryBean,用於生成適配器Bean --entity ----Text.java 編碼資源類的接口 ----Base64Entity.java Base64編碼 ----UrlBase64Entity.java urlBase64編碼 --adapter ----DecodeAdapter.java 解碼適配器接口 ----Base64Adapter.java Base64的解碼適配器 ----UrlBase64Adapter.java UrlBase64的解碼適配器
另外配置文件在 resources/xml/factorybean/config.xml:this
<beans> <!-- Base64編碼 --> <bean class="chkui.springcore.example.xml.factorybean.entity.Base64Entity"> <constructor-arg value="一串加密的文字。URLBase64和Base64的區別是調整了能夠用於URL的符號,例如+替換爲-。"/> </bean> <!-- UrlBase64編碼 --> <!-- <bean class="chkui.springcore.example.xml.factorybean.entity.UrlBase64Entity"> <constructor-arg value="一串加密的文字。URLBase64和Base64的區別是調整了能夠用於URL的符號,例如+替換爲-。"/> </bean> --> <bean id="adapter" class="chkui.springcore.example.xml.factorybean.AdapterFactory" /> </beans>
Base64Entity和UrlBase64Entity是2個資源類,分別用Base64和UrlBase64對字符串進行編碼,經過配置來管理。下面是Text和Base64Entity的代碼:編碼
package chkui.springcore.example.xml.factorybean.entity; //文本資源接口 public interface Text { //定義資源類型,目前支持Base64和UrlBase642種加密編碼文件 public static enum Type{ Base64, UrlBase64 } //獲取資源編碼類型 Type getType(); //獲取編碼的密文 String getCipher(); }
package chkui.springcore.example.xml.factorybean.entity; public class Base64Entity implements Text { private String cipher; public Base64Entity(String text) { this.cipher = Base64.getEncoder().encodeToString(text.getBytes()); } @Override public Type getType() { return Text.Type.Base64; } @Override public String getCipher() { return cipher; } }
而後咱們根據不一樣的資源定義了不一樣的適配器來解碼,下面是適配器接口和一個實現類——DecodeAdapter、Base64Adapter:加密
package chkui.springcore.example.xml.factorybean.adapter; //加密編碼文件解碼適配器 public interface DecodeAdapter { //獲取解碼以後的明文 String getPlain(); }
package chkui.springcore.example.xml.factorybean.adapter; public class Base64Adapter implements DecodeAdapter { private String cipher; public Base64Adapter(String cipher){ this.cipher = cipher; } @Override public String getPlain() { return new String(Base64.getDecoder().decode(cipher)); } }
最後是核心的FactoryBean——AdapterFactory,他的做用是根據當前向IoC添加的資源類型來肯定啓用哪一個適配器。AdapterFactory繼承了BeanFactoryAware以便得到BeanFactory實例:
public class AdapterFactory implements FactoryBean<DecodeAdapter>, BeanFactoryAware { private Text text; private volatile DecodeAdapter adapter; @Override public DecodeAdapter getObject() throws Exception { //根據IoC中的資源類型選擇適配器,懶加載模式 return lazyLoadAdapter(); } @Override public Class<DecodeAdapter> getObjectType() { return DecodeAdapter.class; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.text = beanFactory.getBean(Text.class); } private DecodeAdapter lazyLoadAdapter() { if (null == adapter) { synchronized (AdapterFactory.class) { if (null == adapter) { switch (text.getType()) { case UrlBase64: adapter = new UrlBase64Adapter(text.getCipher()); break; case Base64: default: adapter = new Base64Adapter(text.getCipher()); break; } } } } return this.adapter; } }
lazyLoadAdapter方法實現了適配的過程——根據不一樣的編碼類型返回不一樣的適配器。最後運行容器:
package chkui.springcore.example.xml.factorybean; public class BeanFactoryApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("xml/factorybean/config.xml"); Text text = context.getBean(Text.class); System.out.println("密文:" + text.getCipher()); System.out.println("編碼類型:" + text.getType()); DecodeAdapter decode = context.getBean(DecodeAdapter.class); System.out.println("明文:" + decode.getPlain()); } //經過符號規則獲取工廠Bean private static void nameSymbol(ApplicationContext context) { Object adapter = context.getBean("adapter");//獲取實際Bean System.out.println("adapterClass :" + adapter.getClass().getName()); adapter = context.getBean("&adapter");//獲取實際工廠Bean System.out.println("adapterClass :" + adapter.getClass().getName()); } }
實際上,Spring的全部預設Bean都是經過FactoryBean實現的,如今大概有50多個Spring官方實現的FactoryBean。
注意nameSymbol方法中的代碼和BeanFactory的配置——<bean id="adapter" class="chkui.springcore.example.xml.factorybean.AdapterFactory" />。若是爲BeanFactory指定了ID或別名,那麼經過ID獲取到的是工廠生成Bean而不是這個工廠自己。可是能夠經過在以前增長"&"符號來告訴IoC獲取BeanFactory自己。