Fork me on GitHub

【spring源码系列】之【FactoryBean类型的接口】

 


1.概述

目前我们知道,spring创建bean有多种方式,比如xml方式创建,比如@Component,@Service,@Controler,@Repository注解创建,比如@Autowired依赖注入创建,后续还有通过springboot方式的配置注解@Configuration与@Bean方式结合创建,这里不一一介绍,等分析spring boot源码的时候再做总结。

就spring本身,提供了一种接口方式创建bean,就是本节要讨论的通过FactoryBean接口方式创建。

2.实例

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本身的实例。

3.源码

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。

4.总结

灵活创建所需实例对象的时候,通过实现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的初始化。


__EOF__

本文作者小猪爸爸
本文链接https://www.cnblogs.com/father-of-little-pig/p/16367549.html
关于博主:不要为了技术而技术,总结分享技术,感恩点滴生活!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小猪爸爸  阅读(626)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示