Spring- FactoryBean使用/原理
概要
在某些情况下,实例化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返回的对象不是指定类的一个实例,而是Factory Bean#getObject方法返回的对象。若想获取Factory Bean本身,要在bean的名称添加前缀&来获取FactoryBean对象本身(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不遵循Spring的生命周期
Spring的作者想要放权给使用者,让使用者自己实现创建一个bean的逻辑,所以Spring并不会过多的插手该Bean的实例化过程,使得一个Bean的实例化完全由使用者本人去实现。
这个类并不会像普通bean那样在Spring容器初始化时进行实例化,而是类似于懒加载,在获取时才进行创建和返回。至于是不是单例,要取决于isSingleton()方法的返回值。
二 、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的工作原理。
在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 }
参考链接:https://juejin.cn/post/6844903954615107597
标签:容器,Spring,beanName,bean,FactoryBean,CustomerFactoryBean,UserService From: https://www.cnblogs.com/hld123/p/18255964