Springboot扩展点系列_FactoryBean

功能特性

1、FactoryBean接口有三个方法:1、getObject(),用于获取bean,主要应用在创建一些复杂的bean的场景;2、getObjectType(),用于获取返回bean的类型;3、isSingleton(),用于判断返回bean是否属于单例,默认trure,通俗说就是工厂bean;

2、BeanFactory是Spring bean容器的根接口,ApplicationContext是Spring bean容器的高级接口,继承于BeanFactory,通俗理解就是生成bean的工厂;

所以FactoryBean与BeanFactory本质上是完全不同的两个接口。既然完全不同,又说什么区别呢?至少我是这么认为的。

实现方式

那么FactoryBean怎么用呢?这里先举一个场景:假如需要要创建一个特别复杂的类Computer

1、定义Computer类;

@Slf4j
public class Computer {
    private String type;
 
    public String getType() {
        return type;
    }
 
    public void setType(String type) {
        this.type = type;
    }
 
    public Computer() {
        log.info("----Computer类无参数构造方法触发执行");
    }
 
    public Computer(String type) {
        this.type = type;
        log.info("----Computer类有参数构造方法触发执行");
    }
}

2、定义ComputerFactoryBean,实现FactoryBean接口,实现了getObject(),创建了一个“复杂”的Bean;在getObjectType()返回了Bean的类型;在isSingleton()指定Bean的作用域为单例;

@Component
@Slf4j
public class ComputerFactoryBean implements FactoryBean {
    private String name = "ComputerFactoryBean本尊";
 
    public ComputerFactoryBean() {
        log.info("----ComputerFactoryBean无参数构造方法触发执行");
    }
 
    @Override
    public Object getObject() throws Exception {
        log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-start");
        Computer computer = new Computer("商用笔记本电脑");
        log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-end");
        return computer;
    }
 
    @Override
    public Class<?> getObjectType() {
        return Computer.class;
    }
 
    @Override
    public boolean isSingleton() {
        return true;
    }
}

3、编写单元测试,从Spring容器中取出beanName为"computeFactoryBean"的bean,通常情况下,如果ComputerFactoryBean未实现FactoryBean接口,getBean(“computeFactoryBean”)的结果是computeFactoryBean对象;而实现了FactoryBean接口,getBean(“computeFactoryBean”)的结果的是getObject()方法中返回的结果,即computer,这就是FactoryBean如此神奇的地方。

@Test
public void test6() {
    log.info("----单元测试执行开始");
    log.info("----Spring容器实例化开始");
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
    log.info("----Spring容器实例化完成");
    ComputerFactoryBean computerFactoryBean = ((ComputerFactoryBean) context.getBean("&computerFactoryBean"));
    Computer computer = (Computer) context.getBean("computerFactoryBean");
    Assert.isTrue(computerFactoryBean.getClass().getName().equals("com.fanfu.bean.ComputerFactoryBean"));
    Assert.isTrue(computer.getClass().getName().equals("com.fanfu.entity.Computer"));
    log.info("----单元测试执行完毕");
}

单元测试结果:

img

工作原理

从单元测试验证结果来看,ComputerFactoryBean本尊在Spring容器实例化的过程中,就以非懒加载的单例bean实例化完成注册到了Spring容器里。顺着context.getBean(“computerFactoryBean”)往下跟踪,会发现接着调用了AbstractApplicationContext#getBean(java.lang.String)–>AbstractBeanFactory#getBean(java.lang.String)–>AbstractAutowireCapableBeanFactory#getObjectForBeanInstance()–>AbstractBeanFactory#getObjectForBeanInstance(),到这是一个关键点:

1、AbstractBeanFactory#getObjectForBeanInstance()中判断是否bean是否是一个工厂引用,即beanName是否是“&”开头,如果beanName是以“&”开头,则直接返回本尊;如果不是,则继续向下执行;

2、检查bean是否实现了FactoryBean接口,如果获取的bean没有实现FactoryBean接口,则直接返回;如果获取的bean实现了FactoryBean接口,则继续向下执行;

3、对获取的bean强制转换为FactoryBean,然后去执行FactoryBean接口实现类的getObject();

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    //检查bean是否是一个工厂引用,即beanName是否是“&”开头
   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
      }
      if (mbd != null) {
         mbd.isFactoryBean = true;
      }
      //如果beanName是以“&”开头,则直接返回本尊
      return beanInstance;
   }
    //如果获取的bean没有实现FactoryBean接口,则直接返回;
   if (!(beanInstance instanceof FactoryBean)) {
      return beanInstance;
   }
   Object object = null;
   if (mbd != null) {
      mbd.isFactoryBean = true;
   }
   else {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // 如果获取的bean实现FactoryBean接口,则对bean进行强制转换
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      //去执行FactoryBean接口实现类的getObject()
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

总结

总体来讲,从FactoryBean的实现方式到工作原理,都很简单,同时也是有大用处的扩展点,特别要必要牢牢掌握。当然,如果有面试官再问你,FactoryBean与BeanFactory有什么区别的时候?不要再犹豫,直接告诉他:”FactoryBean与BeanFactory本身并没有什么区别,但是我可以分别给你介绍介绍这两个接口有什么用处。FactoryBean是一个工厂Bean,在需要创建比较复杂的bean的时候可以用到,BeanFactory是Spring bean容器的根接口,也就是说实现BeanFactory,可以得到一个最基础的Spring容器,Spring中的所有高级容器都继承了这个根接口。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值