Springboot扩展点之FactoryBean

傷城~ 2024-03-26 08:40 96阅读 0赞

Springboot扩展点系列:

Springboot扩展点之ApplicationContextInitializer

Springboot扩展点之BeanFactoryPostProcessor

Springboot扩展点之BeanDefinitionRegistryPostProcessor

Springboot扩展点之BeanPostProcessor

Springboot扩展点之InstantiationAwareBeanPostProcessor

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

Springboot扩展点之ApplicationContextAwareProcessor

Springboot扩展点之@PostConstruct

Springboot扩展点之InitializingBean

Springboot扩展点之SmartInitializingSingleton

Springboot扩展点之CommandLineRunner和ApplicationRunner

Springboot扩展点之FactoryBean

Springboot扩展点之DisposableBean

Springboot扩展点系列之终结篇:Bean的生命周期


前言

FactoryBean是一个有意思,且非常重要的扩展点,之所以说是有意思,是因为它老是被拿来与另一个名字比较类似的BeanFactory来比较,特别是在面试当中,动不动就问你:你了解Beanfactory和FactoryBean的区别吗?其实两个是完全不同的接口,如果非要说出有什么明显区别,大概就是名字吧。为什么又说非常重要呢?那是因为在创建一些比较复杂的bean的时候,常规的方式不能使用,就可以考虑使用FactoryBean,特别其他框架技术与Spring集成的时候,如mybatis与Spring的集成,大家都知道,mybatis是通过SqlSessionFactory创建出Sqlsession来执行sql的,那么Service层在调用Dao层的接口来执行数据库操作时肯定得持有SqlSessionFactory,那么问题来了:Spring容器怎么才能持有SqlSessionFactory呢?答案就是SqlSessionFactoryBean,它实现了FactoryBean接口。

既然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类;

  1. @Slf4j
  2. public class Computer {
  3. private String type;
  4. public String getType() {
  5. return type;
  6. }
  7. public void setType(String type) {
  8. this.type = type;
  9. }
  10. public Computer() {
  11. log.info("----Computer类无参数构造方法触发执行");
  12. }
  13. public Computer(String type) {
  14. this.type = type;
  15. log.info("----Computer类有参数构造方法触发执行");
  16. }
  17. }

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

  1. @Component
  2. @Slf4j
  3. public class ComputerFactoryBean implements FactoryBean {
  4. private String name = "ComputerFactoryBean本尊";
  5. public ComputerFactoryBean() {
  6. log.info("----ComputerFactoryBean无参数构造方法触发执行");
  7. }
  8. @Override
  9. public Object getObject() throws Exception {
  10. log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-start");
  11. Computer computer = new Computer("商用笔记本电脑");
  12. log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-end");
  13. return computer;
  14. }
  15. @Override
  16. public Class<?> getObjectType() {
  17. return Computer.class;
  18. }
  19. @Override
  20. public boolean isSingleton() {
  21. return true;
  22. }
  23. }

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

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

单元测试结果:

d6bf43b494404c55a02c33454d4fad24.png

工作原理

从单元测试验证结果来看,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();

  1. protected Object getObjectForBeanInstance(
  2. Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
  3. //检查bean是否是一个工厂引用,即beanName是否是“&”开头
  4. if (BeanFactoryUtils.isFactoryDereference(name)) {
  5. if (beanInstance instanceof NullBean) {
  6. return beanInstance;
  7. }
  8. if (!(beanInstance instanceof FactoryBean)) {
  9. throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
  10. }
  11. if (mbd != null) {
  12. mbd.isFactoryBean = true;
  13. }
  14. //如果beanName是以“&”开头,则直接返回本尊
  15. return beanInstance;
  16. }
  17. //如果获取的bean没有实现FactoryBean接口,则直接返回;
  18. if (!(beanInstance instanceof FactoryBean)) {
  19. return beanInstance;
  20. }
  21. Object object = null;
  22. if (mbd != null) {
  23. mbd.isFactoryBean = true;
  24. }
  25. else {
  26. object = getCachedObjectForFactoryBean(beanName);
  27. }
  28. if (object == null) {
  29. // 如果获取的bean实现FactoryBean接口,则对bean进行强制转换
  30. FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
  31. if (mbd == null && containsBeanDefinition(beanName)) {
  32. mbd = getMergedLocalBeanDefinition(beanName);
  33. }
  34. boolean synthetic = (mbd != null && mbd.isSynthetic());
  35. //去执行FactoryBean接口实现类的getObject()
  36. object = getObjectFromFactoryBean(factory, beanName, !synthetic);
  37. }
  38. return object;
  39. }

总结

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

如果你能这样回答这个问题,相信会给面试官留下一下好印象的。

发表评论

表情:
评论列表 (有 0 条评论,96人围观)

还没有评论,来说两句吧...

相关阅读