(一)BeanFactory
BeanFactory是一个工厂类接口,定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,职责包括实例化、定位、配置应用程序中的对象以及建立这些对象之间的依赖。在Spring中有很多具体的实现,在这些实现中对BeanFactory进行了功能扩展,例如:
- AbstractBeanFactory:抽象Bean工厂,绝大部分的实现类,都是继承于他
- DefaultListableBeanFactory:Spring默认的工厂类
- XmlBeanFactory:前期使用XML配置用的比较多的时候用的Bean工厂
- AbstractXmlApplicationContext:抽象应用容器上下文对象
- ClassPathXmlApplicationContext:XML解析上下文对象,用户创建Bean对象我们早期写Spring的时候用的就是他
(二)FactoryBean
FactoryBean是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。一个Bean如果实现了FactoryBean接口,那么根据该Bean的名称获取到的实际上是getObject返回的对象,而不是这个Bean自身实例,如果要获取这个Bean自身实例,那么需要在名称前面加上'&'符号。
一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式
下面通过代码练习如何使用FactoryBean,以此加深对上面描述内容的理解。
1)TestBean
实现FactoryBean接口并实现getObject()和getObjectType()方法,其中在getObject()方法中实例化一个TestBean对象,并对message进行了一些特殊处理(区别于构造方法)
@Component
public class TestBean implements FactoryBean {
private String message;
public TestBean() {
this.message = "通过构造方法初始化实例";
}
@Override
public Object getObject() throws Exception {
TestBean testBean = new TestBean();
testBean.message = "通过FactoryBean.getObject()创建实例";
// 这里并不一定要返回TestBean自身的实例,可以是其他任何对象的实例
return testBean;
}
@Override
public Class<?> getObjectType() {
return TestBean.class;
}
public String getMessage() {
return message;
}
}
2)FactoryBeanTest测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
TestBean myBean1 = (TestBean) context.getBean("testBean");
System.out.println("myBean1 = " + myBean1.getMessage());
TestBean myBean2 = (TestBean) context.getBean("&testBean");
System.out.println("myBean2 = " + myBean2.getMessage());
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}
3)测试输出
myBean1 = 通过FactoryBean.getObject()初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false
4)总结
可见当调用getBean("testBean")时,Spring通过反射机制发现TestBean实现了FactoryBean接口,这时Spring容器就调用接口方法TestBean#getObject()方法返回,这时返回的并不是FactoryBean本身,而是FactoryBean#getObject()方法内部所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
如果希望获取TestBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&testBean")。