9、Mini-spring FactoryBean

92 阅读3分钟

上一节,我们完善了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"

配合理解

blog.csdn.net/Roger_CX/ar…