Spring核心——FactoryBean

本文繼續以前的2篇文章(BeanPostProcessorBeanFactoryPostProcessor)介紹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

  1.  容器啓動以後會加載一個密文資源類,多是Base64的編碼,也多是UrlBase64的編碼,根據配置來肯定。
  2.  FactoryBean會根據資源類型向容器添加一個解碼的適配器。
  3.  最後用適配器解碼輸出。

例子的代碼結構以下: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自己。

相關文章
相關標籤/搜索