上一节,我们完善了prototype作用域的bean,接下来继续完善项目,增加FactoryBean的实现。
FactoryBean
FactoryBean是一种特殊的Bean。相信学到这里,你应该能仅凭这一句话了解FactoryBean与BeanFactory的区别吧,尽管这两个东西名称看上去很相似。 因为之前我们说的所有的关于IOC的东西,都是从BeanFactory延伸出去的,BeanFactory是整个IOC框架结构的顶级接口,ApplicaitonContext也是基于BeanFactory去实现,BeanFactory作为IOC的容器存在。而回到FactoryBean,它是一种特殊的Bean,当向容器中获取该Bean时,容器返回的不是其本身,而是返回其FactoryBean#getObject方法的返回值。可以通过编码的方式定义复杂的bean。
实现
- 知道了具体的思路后,实现起来也很简单的,就是在getBean()的时候判断下如果是FactoryBean类型的时候就调用其的getObject方法返回最终的bean。
首先定义FactoryBean
public interface FactoryBean<T> {
T getObject() throws Exception;
boolean isSingleton();
}
定义实现FactoryBean的接口CarFactoryBean
public class CarFactoryBean implements FactoryBean<Car> {
private String brand;
@Override
public Car getObject() throws Exception {
Car car = new Car();
car.setBrand(brand);
return car;
}
@Override
public boolean isSingleton() {
return true;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
定义xml文件
<bean id="car" class="org.springframework.test.ioc.common.CarFactoryBean">
<property name="brand" value="porsche"/>
</bean>
测试类:
@Test
public void testFactoryBean() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:factory-bean.xml");
Car car = applicationContext.getBean("car", Car.class);
assertThat(car.getBrand()).isEqualTo("porsche");
}
上面都是一些新增的实现,具体的实现内容则在AbstractBeanFactory中的getBean()方法。这里贴了上节的代码,可以看到主要的区别就是在返回值的处理,从原来的单纯返回Bean,变成了现在的getObjectForBeanInstance()方法。这个方法其实就是去判断如果是BeanFactory的话,就调用它的getObject()方法。
另外一个需要说的地方就是AbstractBeanFactory中新增factoryBeanObjectCache作为单例的缓存。
//lately code 实现FactoryBean
@Override
public Object getBean(String name) throws BeansException {
Object sharedInstance = getSingleton(name);
if (sharedInstance != null) {
//如果是FactoryBean,从FactoryBean#getObject中创建bean
return getObjectForBeanInstance(sharedInstance, name);
}
BeanDefinition beanDefinition = getBeanDefinition(name);
Object bean = createBean(name, beanDefinition);
return getObjectForBeanInstance(bean, name);
}
//old code
@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}
我们来具体看下这个getObjectForBeanInstance()。顺便说一下,这里要是去看Spring源码的话,可以发现里面还有如果对象是&开头的话,返回的还是bean本身,这个方法看Spring源码也很清晰容易理解。
首先判断如果是FactoryBean的话,如果是单例的就从缓存中先获取,获取不到的话,调用getObject()方法,然后将获取到的bean存入缓存中。代码是比较清晰的。
ps:这里我在梳理的时候,一开始没有搞清楚,getBean()方法在获取到sharedInstance之后,判断如果sharedInstance不为空还需要再调用下getObjectForBeanInstance()方法,我以为之前已经存入缓存中了,这里不需要重新调用getObjectForBeanInstance()方法了,当sharedInstance不为空的时候就直接返回从缓存中获得到的对象即可了。实际上这是错误的理解,首先getSingleton()从缓存中获取的数据是singletonObjects的,而实际上我们需要的bean是被存储在factoryBeanObjectCache的,这两个缓存是不一样的,也就是说其实sharedInstance 不是一个完整的bean,还需要后续的方法进行操作。
protected Object getObjectForBeanInstance(Object beanInstance, String beanName) {
Object object = beanInstance;
if (beanInstance instanceof FactoryBean) {
FactoryBean factoryBean = (FactoryBean) beanInstance;
try {
if (factoryBean.isSingleton()) {
//singleton作用域bean,从缓存中获取
object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = factoryBean.getObject();
this.factoryBeanObjectCache.put(beanName, object);
}
} else {
//prototype作用域bean,新创建bean
object = factoryBean.getObject();
}
} catch (Exception ex) {
throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", ex);
}
}
return object;
}
通过测试代码,我们可以实现,通过FactoryBean的方式,去获取创建"Bean"
配合理解