本文继续以前的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自己。