

【spring源码系列】之【FactoryBean类型的接口】 - 小猪爸爸
source link: https://www.cnblogs.com/father-of-little-pig/p/16367549.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【spring源码系列】之【FactoryBean类型的接口】 - 小猪爸爸 - 博客园
目前我们知道,spring创建bean有多种方式,比如xml方式创建,比如@Component,@Service,@Controler,@Repository注解创建,比如@Autowired依赖注入创建,后续还有通过springboot方式的配置注解@Configuration与@Bean方式结合创建,这里不一一介绍,等分析spring boot源码的时候再做总结。
就spring本身,提供了一种接口方式创建bean,就是本节要讨论的通过FactoryBean
接口方式创建。
FactoryBean接口的实现类FactoryBeanDemo:
package com.wzj.FactoryBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Service; @Service public class FactoryBeanDemo implements FactoryBean { @Override public Object getObject() throws Exception { return new FactoryB(); } @Override public Class<?> getObjectType() { return FactoryB.class; } }
通过FactoryBean实现类,完成自定义类FactoryB的实例化,FactoryB:
package com.wzj.FactoryBean; import lombok.Data; @Data public class FactoryB { private String name = "wzj"; }
public class TestSpring { @Autowired private ApplicationContext applicationContext; @Test public void testFactoryBean() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); FactoryB factoryB = (FactoryB)applicationContext.getBean("factoryBeanDemo"); System.out.println(factoryB); FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo)applicationContext.getBean("&factoryBeanDemo"); System.out.println(factoryBeanDemo); }
可以看出,当获取名称为factoryBeanDemo
的实例时,得到的是getObject()方法里创建的FactoryB类型的对象,而获取加前缀&
的factoryBeanDemo
的实例时,得到的是FactoryBeanDemo本身的实例。
step1
: FactoryBean 接口的调用入口在实例化和 IOC/DI 做完后,就会调用 FactoryBean 类型的接口如下图所示
// Create bean instance. // 创建bean实例 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); // FactoryBean的调用入口 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
step2
: 如果要获取到 FactoryBean 类本身,就必须加上&
符号,比如 beanFactory.getBean("&beanName") ,如下:
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. // 如果为name不为空,且以前缀&打头,直接返回bean本身 if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } if (mbd != null) { mbd.isFactoryBean = true; } return beanInstance; } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beanInstance instanceof FactoryBean)) { return beanInstance; }
public static boolean isFactoryDereference(@Nullable String name) { return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); }
String FACTORY_BEAN_PREFIX = "&";
stet3
: BeanFactory.getBean("beanName")只能获取到 getObject()方法返回的实例。getObject 方法返回的实例会有单独的缓存存储,跟其他实例不是同一个缓存,对应的缓存是:factoryBeanObjectCache
// 如果是不是以前缀&打头,并且是FactoryBean类型的 Object object = null; if (mbd != null) { mbd.isFactoryBean = true; } else { // 从缓存里拿FactoryBean实例 object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); // 缓存没有的话, object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // 先从缓存factoryBeanObjectCache取 Object object = this.factoryBeanObjectCache.get(beanName); // 如果缓存为空, if (object == null) { // 调用getObject方法 object = doGetObjectFromFactoryBean(factory, beanName); // Only post-process and store if not put there already during getObject() call above // (e.g. because of circular reference processing triggered by custom getBean calls) Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { // Temporarily return non-post-processed object, not storing it yet.. return object; } beforeSingletonCreation(beanName); try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { afterSingletonCreation(beanName); } } if (containsSingleton(beanName)) { // 最后放到缓存中 this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } ......
小结:具体代码参考 getSingleton 方法之后 getObjectForBeanInstance
- 如果bean实例不是 FactoryBean 类型的或者 name 以&开始的则直接返回实例。
- 如果bean是 FacotyBean 并且不是以&开头, 会通过方法doGetObjectFromFactoryBean 调用FactoryBean 内部继承实现的 getObject 方法,并且判断一级缓存中如果存在该 bean 实例把实例缓存到factoryBeanObjectCache 对应的 map 中,这个是单独缓存 FactoryBean 类型实例的 map。
灵活创建所需实例对象的时候,通过实现FactoryBean
接口的getObject
方法定义实例化过程。
比如MyBatis
提供mybatis-spring
项目中的 org.mybatis.spring.SqlSessionFactoryBean
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { // ...省略其他代码 public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; } }
sqlSessionFactory是SqlSessionFactoryBean的一个属性,它的赋值是在通过回调afterPropertiesSet()方法进行的。 因为SqlSessionFactoryBean实现了InitializingBean接口,所以在Spring初始化Bean的时候,能回调afterPropertiesSet()方法。
public void afterPropertiesSet() throws Exception { // buildSqlSessionFactory()方法会根据mybatis的配置进行初始化。 this.sqlSessionFactory = buildSqlSessionFactory(); }
在上面的afterPropertiesSet()方法中,buildSqlSessionFactory()方法会根据mybatis的配置,完成客户所需要的的sessionFactory的初始化。
Recommend
-
53
BeanFactory这个其实是所有Spring Bean的容器根接口,给Spring 的容器定义一套规范,给IOC容器提供了一套完整的规范,比如我们常用到的getBean方法等The root interface for accessing a Spring bean container. This is the basic client view of a bean containe...
-
20
本文是《再读Spring源码》系列第二篇,一起来探讨下Spring框架常见扩展点FactoryBean,了解它的使用场景和实现原理。 FactoryBean简介正如名字所言,FactoryBean本质上一个是工厂类,用于构造用户所需的Bean。通过FactoryBean可以隐藏到构造...
-
13
还不知道FactoryBean有啥用?探寻FactoryBean的究极奥义之从Spring+MyBatis扫描源码说起万字长文警告 !建议在首页看的,点击标题进入文章页查看,好看点。本文章又名: 兼容 Spring 体系的 java 框架实现妙计前排先提示一波:
-
6
如果你的简历上这么写,熟练掌握Spring,并对Spring源码有一定的见解(或者是读过Spring源码),那么无法避免的就是要问几个问题。 IOC的实现原理是什么? AOP底层是如何实现的? 因为这些基本上都是面试必然要准备的题目,所以如果你能...
-
6
Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?发布于 今天 14:00 ApplicationContext接口,它由BeanFactory接口派生而来。ApplicationContext包含Bean...
-
3
Spring系列(七):@FactoryBean注解用法介绍 原创 IT技术分享社区 2022-03-16 12:59:19...
-
7
自己动手写spring(六) 支持FactoryBean 为什么需要工厂bean?回答这个问题,首先要回答下为什么需要工厂模式?简单说,就是不用new xxx()...
-
6
V2EX › Java 请问一下,在实现 spring 的 FactoryBean 时,如何得到类成员方法里某个局部变量的真实类型?
-
6
Spring 中 BeanFactory 和 FactoryBean 有何区别? 以下内容基于 Spring6.0.4。 这也是 Spring 面试时一道经典的面试问题,今天我们来聊一聊这个话...
-
5
3 个月前 Spring ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK