Spring容器系列-FactoryBean使用/原理

Spring- FactoryBean使用/原理

    概要

    一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean。在某些情况下,实例化Bean过程比较复杂,若按照传统的方式,则需要提供大量的配置信息,不够灵活,这时采用编码的方式能得到一个简单的方案。

    Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑

    FactoryBean接口在Spring中占重要地位,Spring自身就提供了70多个Factory Bean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

    从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式。

    一、普通bean与FactoryBean的区别

    Spring容器中有两种bean:普通bean和工厂bean。

    1. 普通bean

   通过反射来实例化被标记为bean的类。例:@Component指定的类。

    2. FactoryBean

    FactoryBean是一个能生产或修饰对象生成的工厂Bean。一个Bean如果实现了FactoryBean接口,那么根据该Bean的名称获取到的实际上是getObject()返回的对象,而不是这个Bean自身实例,如果要获取这个Bean自身实例,那么需要在名称前面加“&”符号。比如:applicationContext.getBean("&"+beanName) 

    FactoryBean接口源码:

 1 public interface FactoryBean<T>{
 2    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
 3 
 4    @Nullable
 5    T getobject()throws Exception;
 6 
 7    @Nullable
 8    Class<?> getobjectType();
 9 
10    default boolean isSingleton(){
11       return true;
12    }
13 }

   3. FactoryBean的生命周期

   FactoryBean 自己是一个工厂类,它不直接创建 Bean,而是通过 getObject() 方法来返回实际的 Bean 实例。

   1)FactoryBean 本身的生命周期与容器管理的一般 Bean 类似。

   2)FactoryBean 创建的对象(实际的 Bean)由容器管理,其生命周期由 Bean 的类型和作用域决定。

   FactoryBean的特点:

 

  • 放权给用户:FactoryBean 允许用户定义 Bean 的实例化逻辑。
  • 懒加载:FactoryBean 本身不会在容器启动时实例化,而是延迟到调用 getObject() 时创建。
  • 单例判断:isSingleton() 方法决定了 FactoryBean 返回的 Bean 是否为单例。

 

   二 、FactoryBean的用法

   FactoryBean的特殊之处在于它可以向容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。先通过如下示例代码来感受下FactoryBean的用处吧。

   自定义一个类CustomerFactoryBean,让它实现了FactoryBean接口,重写了接口中的两个方法,在getObejct()方法中,返回了一个UserService的实例对象;在getObjectType()方法中返回了UserService.class。

   然后在CustomerFactoryBean添加了注解@Component注解,意思是将CustomerFactoryBean类交给Spring管理。

   UserService类:

1 public class UserService {
2     public UserService() {
3         System.out.println("userService construct");
4     }
5 }

   CustomerFactoryBean类:

 1 package org.example.factory;
 2 
 3 import org.example.service.UserService;
 4 import org.springframework.beans.factory.FactoryBean;
 5 import org.springframework.stereotype.Component;
 6 
 7 /**
 8  * @author hxq
 9  * @date 2024/6/19 11:07
10  */
11 @Component
12 public class CustomerFactoryBean  implements FactoryBean<UserService> {
13     public UserService getObject() throws Exception {
14         return new UserService();
15     }
16 
17     public Class<?> getObjectType() {
18         return UserService.class;
19     }
20 }

   AppConfig类:

1 @Configuration
2 @ComponentScan("org.example.factory")
3 public class AppConfig {
4 
5 }

  启动类:

 1 public class App {
 2     public static void main(String[] args) {
 3         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
 4         System.out.println("容器启动完成");
 5         CustomerFactoryBean rawBean = (CustomerFactoryBean) applicationContext.getBean("&customerFactoryBean");
 6         System.out.println(rawBean);
 7 
 8         UserService userService = applicationContext.getBean(UserService.class);
 9         System.out.println(userService);
10         Object customerFactoryBean = applicationContext.getBean("customerFactoryBean");
11         System.out.println(customerFactoryBean);
12     }
13 }
14 
15 # 运行结果
16 容器启动完成
17 org.example.factory.CustomerFactoryBean@35fc6dc4
18 userService construct
19 org.example.service.UserService@1dd92fe2
20 org.example.service.UserService@1dd92fe2

   从上面例子可以看出,FactoryBean是一个特殊的Bean。我们自定义的CustomerFactoryBean实现了FactoryBean接口,所以当CustomerFactoryBean被扫描进Spring容器时,实际上它向容器中注册了两个bean,一个是CustomerFactoryBean类的单例对象;另外一个就是getObject()方法返回的对象。

   三、FactoryBean的源码

   通过上面的示例代码,我们知道了FactoryBean的作用,也知道该如何使用FactoryBean,那么接下来我们就通过源码来看看FactoryBean的工作原理。

   1. preInstantiateSingletons

   在Spring容器启动阶段,会调用到refresh()方法,在refresh()中有调用了finishBeanFactoryInitialization()方法,最终会调用到beanFactory.preInstantiateSingletons()方法。所以我们先看下这个方法的源码。

   DefaultListableBeanFactory#preInstantiateSingletons方法:

 1 public void preInstantiateSingletons() throws BeansException {
 2     // 从容器中获取到所有的beanName
 3     List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
 4     for (String beanName : beanNames) {
 5         RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 6         if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
 7 
 8             // 在此处会根据beanName判断bean是不是一个FactoryBean,实现了FactoryBean接口的bean,会返回true
 9             // 此时当beanName为customerFactoryBean时,会返回true,会进入到if语句中
10             if (isFactoryBean(beanName)) {
11                 // 然后通过getBean()方法去获取或者创建单例对象
12                 // 注意:在此处为beanName拼接了一个前缀:FACTORY_BEAN_PREFIX
13                 // FACTORY_BEAN_PREFIX是一个常量字符串,即:&
14                 // 所以在此时容器启动阶段,对于customerFactoryBean,应该是:getBean("&customerFactoryBean")
15                 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
16 
17 
18                 // 下面这一段逻辑,是判断是否需要在容器启动阶段,就去实例化getObject()返回的对象,即是否调用FactoryBean的getObject()方法
19                 if (bean instanceof FactoryBean) {
20                     final FactoryBean<?> factory = (FactoryBean<?>) bean;
21                     boolean isEagerInit;
22                     if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
23                         isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
24                                         ((SmartFactoryBean<?>) factory)::isEagerInit,
25                                 getAccessControlContext());
26                     }
27                     else {
28                         isEagerInit = (factory instanceof SmartFactoryBean &&
29                                 ((SmartFactoryBean<?>) factory).isEagerInit());
30                     }
// 指示工厂是否在容器启动时立即创建对象(即急切初始化)
31 if (isEagerInit) { 32 getBean(beanName); 33 } 34 } 35 } 36 else { 37 getBean(beanName); 38 } 39 } 40 } 41 }

  在容器启动阶段,会先通过getBean()方法来创建CustomerFactoryBean的实例对象。如果实现了SmartFactoryBean接口,且isEagerInit()方法返回的是true,那么在容器启动阶段,就会调用getObject()方法,向容器中注册getObject()方法返回值的对象。否则,只有当第一次获取getObject()返回值的对象时,才会去回调getObject()方法。

  2. doGetBean

  AbstractBeanFactory#doGetBean方法

  在getBean()中会调用到doGetBean()方法,下面为doGetBean()精简后的源码。从源码中我们发现,最终都会调用getObjectForBeanInstance()方法。

 1 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
 2             @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
 3     final String beanName = transformedBeanName(name);
 4     Object bean;
 5 
 6     Object sharedInstance = getSingleton(beanName);
 7     if (sharedInstance != null && args == null) {
 8         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 9     }
10     else {
11         if (mbd.isSingleton()) {
12             
13             bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
14         }
15         else if (mbd.isPrototype()) {
16             bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
17         }
18         else {
19             bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
20         }
21         
22     }
23     return (T) bean;
24 }

   3. getObjectForBeanInstance

   AbstractBeanFactory#getObjectForBeanInstance方法的部分源码如下:

 1 protected Object getObjectForBeanInstance(
 2         Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
 3 
 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     }
12     // 如果bean不是factoryBean,那么会直接返回Bean
13     // 或者bean是factoryBean但name是以&特殊符号开头的,此时表示要获取FactoryBean的原生对象。
14     // 例如:如果name = &customerFactoryBean,那么此时会返回CustomerFactoryBean类型的bean
15     if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
16         return beanInstance;
17     }
18     // 如果是FactoryBean,那么先从cache中获取,如果缓存不存在,则会去调用FactoryBean的getObject()方法。
19     Object object = null;
20     if (mbd == null) {
21         // 从缓存中获取。什么时候放入缓存的呢?在第一次调用getObject()方法时,会将返回值放入到缓存。
22         object = getCachedObjectForFactoryBean(beanName);
23     }
24     if (object == null) {
25         FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
26         if (mbd == null && containsBeanDefinition(beanName)) {
27             mbd = getMergedLocalBeanDefinition(beanName);
28         }
29         boolean synthetic = (mbd != null && mbd.isSynthetic());
30         // 在getObjectFromFactoryBean()方法中最终会调用到getObject()方法
31         object = getObjectFromFactoryBean(factory, beanName, !synthetic);
32     }
33     return object;
34 }

   4. getObjectFromFactoryBean

   FactoryBeanRegistrySupport#getObjectFromFactoryBean方法:

   说明:该方法中主要是通过调用doGetObjectFromFactoryBean()方法得到bean,然后对bean进行处理,最后放入缓存。而且还会针对单例bean和非单例bean做区分处理,对于单例bean,会在创建完后,将其放入到缓存中,非单例bean则不会放入缓存,而是每次都会重新创建。

 1 protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
 2     // 如果BeanFactory的isSingleton()方法返回值是true,表示getObject()返回值对象是单例的
 3     if (factory.isSingleton() && containsSingleton(beanName)) {
 4         synchronized (getSingletonMutex()) {
 5             // 再一次判断缓存中是否存在。(双重检测机制,和平时写线程安全的代码类似)
 6             Object object = this.factoryBeanObjectCache.get(beanName);
 7             if (object == null) {
 8                 // 在doGetObjectFromFactoryBean()中才是真正调用getObject()方法
 9                 object = doGetObjectFromFactoryBean(factory, beanName);
10                 Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
11                 if (alreadyThere != null) {
12                     object = alreadyThere;
13                 }
14                 else {
15                     // 下面是进行后置处理,和普通的bean的后置处理没有任何区别
16                     if (shouldPostProcess) {
17                         if (isSingletonCurrentlyInCreation(beanName)) {
18                             return object;
19                         }
20                         beforeSingletonCreation(beanName);
21                         try {
22                             object = postProcessObjectFromFactoryBean(object, beanName);
23                         }
24                         catch (Throwable ex) {
25                             throw new BeanCreationException(beanName,
26                                     "Post-processing of FactoryBean's singleton object failed", ex);
27                         }
28                         finally {
29                             afterSingletonCreation(beanName);
30                         }
31                     }
32                     // 放入到缓存中
33                     if (containsSingleton(beanName)) {
34                         this.factoryBeanObjectCache.put(beanName, object);
35                     }
36                 }
37             }
38             return object;
39         }
40     }
41     // 非单例
42     else {
43         Object object = doGetObjectFromFactoryBean(factory, beanName);
44         if (shouldPostProcess) {
45             try {
46                 object = postProcessObjectFromFactoryBean(object, beanName);
47             }
48             catch (Throwable ex) {
49                 throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
50             }
51         }
52         return object;
53     }
54 }

   5. doGetObjectFromFactoryBean

   FactoryBeanRegistrySupport#doGetObjectFromFactoryBean方法

   doGetObjectFromFactoryBean()方法的逻辑比较简单,直接调用了FactoryBean的getObject()方法。部分源码如下:

 1 private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
 2             throws BeanCreationException {
 3 
 4     Object object;
 5     if (System.getSecurityManager() != null) {
 6         AccessControlContext acc = getAccessControlContext();
 7         try {
 8             object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
 9         }
10         catch (PrivilegedActionException pae) {
11             throw pae.getException();
12         }
13     }
14     else {
15         // 调用getObject()方法
16         object = factory.getObject();
17     }
18     return object;
19 }

   6. postProcessObjectFromFactoryBean

   FactoryBeanRegistrySupport#postProcessObjectFromFactoryBean 

1     protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
3         return object;
6     }

   postProcessObjectFromFactoryBean() 是一个钩子方法,是留给子类覆盖的。它允许子类或者自定义的 BeanFactory 进行扩展,以在从 FactoryBean 获取的对象被返回之前对其进行处理或修改。虽然在默认实现中它没有做任何事情,但可以通过继承并重写这个方法来增加额外的逻辑。

   所以我们接着看子类AbstractAutowireCapableBeanFactory对postProcessObjectFromFactoryBean方法的覆写源码。

   先看下AbstractAutowireCapableBeanFactory的结构:

 

   AbstractAutowireCapableBeanFactory#postProcessObjectFromFactoryBean

1     @Override
2     protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
3         return applyBeanPostProcessorsAfterInitialization(object, beanName);
4     }

   四、Spring中的使用场景 - MyBatis

   SqlSessionFactoryBean 是 MyBatis 在 Spring 环境下用来创建 SqlSessionFactory 实例的工厂类。它实现了 Spring 的 FactoryBean 接口,允许 Spring 容器通过其创建和管理 MyBatis 的核心对象 SqlSessionFactory。

  部分源码:

 

 1 public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
 2 
 3     private DataSource dataSource;      // 数据源
 4     private Resource configLocation;    // MyBatis 配置文件位置
 5     private List<Resource> mapperLocations; // Mapper XML 文件的资源
 6     private SqlSessionFactory sqlSessionFactory; // 存储创建的 SqlSessionFactory 实例
 7 
 8     // Setter 方法,用于配置数据源、MyBatis 配置文件、Mapper 文件等
 9     public void setDataSource(DataSource dataSource) {
10         this.dataSource = dataSource;
11     }
12 
13     public void setConfigLocation(Resource configLocation) {
14         this.configLocation = configLocation;
15     }
16 
17     public void setMapperLocations(List<Resource> mapperLocations) {
18         this.mapperLocations = mapperLocations;
19     }
20 
21     // 通过 Spring 的 FactoryBean 接口提供 getObject() 方法来获取 SqlSessionFactory 实例
22     @Override
23     public SqlSessionFactory getObject() throws Exception {
24         if (this.sqlSessionFactory == null) {
25             // 创建并配置 SqlSessionFactory 实例
26             this.sqlSessionFactory = buildSqlSessionFactory();
27         }
28         return this.sqlSessionFactory;
29     }
30 
31     // 返回 SqlSessionFactory 的类型
32     @Override
33     public Class<?> getObjectType() {
34         return SqlSessionFactory.class;
35     }
36 
37     // 默认情况下,SqlSessionFactory 是单例的
38     @Override
39     public boolean isSingleton() {
40         return true;
41     }
42 
43     // 创建 SqlSessionFactory 的核心方法
44     protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
45         // 创建 MyBatis 配置对象
46         Configuration configuration = new Configuration();
47 
48         // 如果配置文件存在,加载配置文件
49         if (this.configLocation != null) {
50             InputStream inputStream = this.configLocation.getInputStream();
51             XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream);
52             configuration = xmlConfigBuilder.parse();
53         }
54 
55         // 设置数据源
56         configuration.setEnvironment(new Environment("development", new JdbcTransactionFactory(), this.dataSource));
57 
58         // 配置 Mapper XML 文件
59         if (this.mapperLocations != null) {
60             for (Resource mapperLocation : this.mapperLocations) {
61                 InputStream inputStream = mapperLocation.getInputStream();
62                 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration, mapperLocation.toString(), configuration.getSqlFragments());
63                 xmlMapperBuilder.parse();
64             }
65         }
66 
67         // 创建 SqlSessionFactory 实例并返回
68         return new DefaultSqlSessionFactory(configuration);
69     }
70 }

 

   MyBatis 在与 Spring 集成时,利用 FactoryBean 简化了 SqlSessionFactory 的创建过程,让开发者只需关注配置而不需要手动管理对象的生命周期。   

   五、 FactoryBean的创建流程

   最后以一张流程图总结下FactoryBean的创建流程:

   

  参考链接:https://juejin.cn/post/6844903954615107597

posted @ 2024-06-19 12:00  欢乐豆123  阅读(63)  评论(0编辑  收藏  举报