spring扩展点五:factoryBean的使用

beanFactory和factoryBean的区别

在说factoryBean的时候,通常会和beanFactory进行对比,这两个名字是比较相似的,但是两者没有任何的关联
beanFactory是操作bean容器的一个接口,提供了getBean()、isSingleton()等方法,我们常说的DefaultListableBeanFactory就是该接口的实现类
factoryBean是spring提供的一个扩展机制,我理解的是:如果我们自己实现了factoryBean接口,那可以自己去初始化一个bean,由spring来完成对bean的实例化,但是初始化的过程,由我们自己控制

spring源码对factoryBean的处理

我们以mybatis和spring整合的时候,所用到的mapperFactoryBean为例,来说明factoryBean是如何使用的

我们前面博客中,有说过,mybatis在和spring整合的时候,在将mapper接口扫描出来,转换为beanDefinition的时候,会把其beanClass设置为mapperFactoryBean,并将自动注入模型设置为2,其中,beanClass设置为mapperFactoryBean就和这里要说的factoryBean有关系,我们来看下设置的代码

org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions

definition.setBeanClass(this.mapperFactoryBean.getClass());

这里既然设置了beanClass为mapperFactoryBean,那总要有地方要用到,在哪里用的呢?

在调用dogetBean()方法尝试从单实例池中获取bean的时候,会对factoryBean进行处理

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

	/**
	 * 通过name获取beanName,这里不使用name直接作为beanName有两个原因:
	 *  1.name可能是以&开头的,表明调用者想获取FactoryBean本身,而非FactoryBean;在beanFactory中factoryBean的存储也是map格式
	 *    <beanName,bean> 只是说,普通的beanName是没有&这个字符串的,所以,需要将name的首字母移除,这样才能从缓存中拿到factoryBean
	 *  2.还是别名的问题,需要转换
	 */
	final String beanName = transformedBeanName(name);
	Object bean;

	/**
	 * 1.从单例池中获取当前bean
	 * 2.这里是循环依赖的重要方法之一
	 * 如果取到的sharedInstance不为null,就表示从单实例池中或者二级缓存中,获取到了bean,就无须进行实例化
	 */
	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isDebugEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		/**
		 * 如果获取到的bean是factoryBean类型的(比如:mybatis的mapper接口就是MapperFactoryBean类型的),就会在下面这行代码中,调用
		 * factoryBean的getObject()方法,完成代理对象的生成或者一些业务逻辑的处理
		 */
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
}

这里可以看到,在从单实例池中获取到bean之后,会调用getObjectForBeanInstance()方法,这个方法中,就是对factorybean的处理
坦白而言,这里处理的逻辑没有完全看懂,所以,就不再往内层深入的说了,后面对这里研究透了,再补充,我们现在可以简单的认为,在这个方法中,会调用factoryBean的getObject()方法,由于mapper接口初始化的beanDefinition设置的factoryBean是MapperFactoryBean,所以,这里会调用mapperFactoryBean.getObject()方法

思考

我们就知道了,spring提供了factoryBean接口,和对factoryBean的处理,至于我们在实现类中是如何写我们自己的业务逻辑的,spring不关心,spring只需要我们保证,在调用factoryBean的getObject()方法的时候,会返回一个对象就可以了,这个对象就是spring需要继续去实例化、属性赋值的对象

个人觉得,factoryBean的主要作用是在第三方框架在和spring整合的时候,可以利用这个扩展点,将第三方框架的class交给spring去管理,因为我们想下,如果mybatis在和spring整合的时候,怎么把一个个接口放到spring容器里面?总不能说在mybatis的源码中,直接依赖于spring的jar包,然后通过@Component注解将mapper注入到spring容器中吧,这样的话,就和spring强耦合了,所以,mybatis就利用了factoryBean这个扩展点,mybatis自己去声明了一个factoryBean的实现类,然后在实现类的getObject()方法中,会返回一个mapper接口所对应的代理对象,然后spring会把这个代理对象,注入到service层,这样的话,我们在调用mapper接口中的方法的时候,实际上调用的是代理对象的invoke方法,然后invoke方法拦截到方法之后,会调用sqlSession的delete、update、select等方法

上面这些,就是自己对factoryBean的一些见解,有其他的想法,后面再随时补充,这个知识点其实也挺简单的,只是觉得单独写一篇笔记记录下来,方便后面随时去记录扩展,就这样

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/115267683
今日推荐