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