面试官:BeanFactory和FactoryBean有哪些区别?

开发 前端
通过分析DefaultListableBeanFactory#preInstantiateSingletons方法和FactoryBean#getObject的调用链路可以分析得到。

[[398320]]

区别

说实话,他俩除了名字比较像以外,好像没有其他共同点了。

「BeanFactory和FactoryBean有哪些区别?」

  1. BeanFactory是一个最基础的IOC容器,提供了依赖查找,依赖注入等基础的功能
  2. FactoryBean是创建Bean的一种方式,帮助实现复杂Bean的创建

和BeanFactory相关的还有一个高频的面试题

「ApplicationContext和BeanFactory有哪些区别?」

  1. BeanFactory是一个最基础的IOC容器,提供了依赖查找,依赖注入等基础的功能
  2. ApplicationContext继承了BeanFactory,在BeanFactory的基础上增加了企业级的功能,如AOP,资源管理(Resources)事件(Event),国际化(i18n),Environment抽象等

创建Bean的方式

常见的创建Bean的方式有如下四种

  1. 通过构造器
  2. 通过静态工厂方法
  3. 通过Bean工厂方法
  4. 通过FactoryBean
  1. @Data 
  2. @ToString 
  3. public class User { 
  4.  
  5.  private Long id; 
  6.  private String name
  7.  
  8.  public static User createUser() { 
  9.   User user = new User(); 
  10.   user.setId(1L); 
  11.   user.setName("li"); 
  12.   return user
  13.  } 
  1. public class UserFactory { 
  2.  
  3.  public User createUser() { 
  4.   return User.createUser(); 
  5.  } 
  1. public class UserFactoryBean implements FactoryBean { 
  2.  
  3.  @Override 
  4.  public Object getObject() throws Exception { 
  5.   return User.createUser(); 
  6.  } 
  7.  
  8.  @Override 
  9.  public Class<?> getObjectType() { 
  10.   return User.class; 
  11.  } 
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans 
  5.         https://www.springframework.org/schema/beans/spring-beans.xsd"> 
  6.  
  7.  <!-- 构造方法实例化 Bean --> 
  8.  <bean id="user-by-constructor" class="com.javashitang.domain.User"
  9.   <property name="id" value="1"/> 
  10.         <property name="name" value="li"/> 
  11.  </bean> 
  12.  
  13.  <!-- 静态方法实例化 Bean --> 
  14.  <bean id="user-by-static-method" class="com.javashitang.domain.User" 
  15.     factory-method="createUser"/> 
  16.  
  17.  <bean id="userFactory" class="com.javashitang.factory.UserFactory"/> 
  18.  
  19.  <!-- Bean工厂方法实例化 Bean --> 
  20.  <bean id="user-by-factory" factory-bean="userFactory" factory-method="createUser"/> 
  21.  
  22.  <!-- FactoryBean实例化 Bean --> 
  23.  <bean id="user-by-factory-bean" class="com.javashitang.factory.UserFactoryBean"/> 
  24. </beans>
  1. public class BeanInstantiationDemo { 
  2.  
  3.  public static void main(String[] args) { 
  4.   BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/bean-instantiation-context.xml"); 
  5.   User user1 = beanFactory.getBean("user-by-constructor"User.class); 
  6.   User user2 = beanFactory.getBean("user-by-static-method"User.class); 
  7.   User user3 = beanFactory.getBean("user-by-factory"User.class); 
  8.   User user4 = beanFactory.getBean("user-by-factory-bean"User.class); 
  9.  } 

实现原理

在分析源码之前,我们先明确2个概念

「factoryBean是我们配置到容器中的实现FactoryBean接口的Bean,而subBean是用FactoryBean创建出来的Bean」

在Spring容器启动的过程中,会实例化非延迟的单例Bean,即调用如下方法 DefaultListableBeanFactory#preInstantiateSingletons

调用FactoryBean#getObject的链路如下图

通过分析DefaultListableBeanFactory#preInstantiateSingletons方法和FactoryBean#getObject的调用链路可以分析得到

  1. 单例的factoryBean对象本身会在spring容器启动时主动初始化。而subBean的初始化则是在第一次从缓存中获取factoryBean并且不为空才会触发
  2. 如果factoryBean对象实现的接口是SmartFactoryBean且isEagerInit方法返回true,那么subBean对象也会在spring容器启动的时候主动初始化
  3. 如果bean注册的时候,beanName对应的bean实例是一个factoryBean,那么我们通过getBean(beanName)获取到的对象将会是subBean对象;如果要获取工厂对象factoryBean,需要使用getBean("&" + beanName)
  4. 单例的subBean也会缓存在spring容器中,具体的容器是FactoryBeanRegistrySupport#factoryBeanObjectCache,一个Map

「建议大家看一下DefaultListableBeanFactory#preInstantiateSingletons方法和FactoryBean#getObject方法的调用链路,就能理解上面我说的流程了,我就不贴太多源码了」

应用

目前我只在Dubbbo源码中看到了FactoryBean的应用

「服务导出:在Dubbo中,服务提供者会被包装成ServiceBean对象,当监听到ContextRefreshedEvent事件时开始服务导出」

「服务调用:服务调用方会被包装成ReferenceBean对象,ReferenceBean实现了FactoryBean接口和InitializingBean接口,创建subBean的逻辑在ReferenceBean#getObject方法中」

「Dubbo服务引入的时机有如下2种。」

  1. 饿汉式:init=true,在Bean生命周期的初始化阶段会调用InitializingBean#afterPropertiesSet方法,而这个方法会调用ReferenceBean#getObject方法,完成subBean的创建,即ReferenceBean实例化时完成服务引入
  2. 懒汉式:init=false,在ReferenceBean对应的服务被注入到其他类中时,此时会调用AbstractApplicationContext#getBean,获取ReferenceBean对象,因为ReferenceBean实现了FactoryBean接口,所以会调用ReferenceBean#getObject方法,完成subBean的创建,即完成服务引入
  1. public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { 
  2.  
  3.     @Override 
  4.     public Object getObject() { 
  5.         return get(); 
  6.     } 
  7.  
  8.     @Override 
  9.     @SuppressWarnings({"unchecked"}) 
  10.     public void afterPropertiesSet() throws Exception { 
  11.  
  12.         // 省略部分代码 
  13.  
  14.         if (shouldInit()) { 
  15.             getObject(); 
  16.         } 
  17.     } 
  18.      

本文转载自微信公众号「Java识堂」,可以通过以下二维码关注。转载本文请联系Java识堂公众号。

 

责任编辑:武晓燕 来源: Java识堂
相关推荐

2023-07-06 14:24:23

Spring接口自定义

2023-02-20 08:08:48

限流算法计数器算法令牌桶算法

2021-07-01 07:51:45

React事件绑定

2021-09-30 07:57:13

排序算法面试

2024-04-03 15:33:04

JWTSession传输信息

2024-09-19 08:42:43

2023-02-17 08:10:24

2024-03-07 17:21:12

HotSpotJVMHot Code

2024-03-12 14:36:44

微服务HTTPRPC

2024-04-19 00:00:00

计数器算法限流算法

2024-02-26 14:07:18

2021-12-13 06:56:45

Comparable元素排序

2021-12-23 07:11:31

开发

2024-03-20 15:12:59

KafkaES中间件

2023-10-16 08:16:31

Bean接口类型

2021-07-02 07:06:20

React组件方式

2021-08-11 08:53:23

Git命令面试

2024-02-01 08:08:53

Spring过滤器类型Gateway

2021-12-10 12:01:37

finalfinallyfinalize

2021-11-30 07:44:50

FinalFinallyFinalize
点赞
收藏

51CTO技术栈公众号