引入
在Spring中存在着BeanFactory和FactoryBean两个接口,很多人容易搞错甚至不知道他们之间的区别。实际上它们的区别特别大,它们俩都不是同一个东西。BeanFactory就是Bean工厂,就是Spring中底层的IOC容器。而FactoryBean是干嘛的呢?
为什么要提供该接口
一般情况下,Spring是通过反射机制利用Bean的Class属性指定实现类来实例化Class。但是在某些情况下,实例化Bean过程比较复杂,传统的方式就不太适合用来实例化Bean。所以Spring提供了FactoryBean这么一个接口,可以通过该接口实现定制实例化Bean的逻辑。接口定义如下:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 返回工厂管理的实例实例对象
*/
T getObject() throws Exception;
/**
* 返回FactoryBean创建的实例的类型
*/
Class<?> getObjectType();
/**
* 是否是单例
*/
default boolean isSingleton() {
return true;
}
}
该定义为Spring-5.2.12.RELEASE版本中的内容。一共就三个接口,作用分别是返回实例对象、类型、是否是单例。
如何使用
上面已经介绍FactoryBean的作用,下面示例展示如何使用。
public class FactoryBeanDemo {
public static void main(String[] args) {
//创建IOC容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//读取配置文件
BeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring-factory-bean.xml");
//获取UserService实例
UserService us1 = (UserService) beanFactory.getBean("userService");
System.out.println(us1);
//获取UserServiceFactoryBean实例
UserServiceFactoryBean usfb = (UserServiceFactoryBean) beanFactory.getBean("&userService");
System.out.println(usfb);
}
}
class UserServiceFactoryBean implements FactoryBean<UserService>{
public UserServiceFactoryBean() {
System.out.println("调用无参构造函数创建UserServiceFactoryBean:"+this);
}
@Override
public UserService getObject() throws Exception {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
class UserService{
public UserService() {
System.out.println("调用无参构造函数创建UserService:"+this);
}
}
spring-factory-bean.xml配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.buydeem.factorybean.UserServiceFactoryBean"/>
</beans>
运行后的打印结果如下:
调用无参构造函数创建UserServiceFactoryBean:com.buydeem.factorybean.UserServiceFactoryBean@627551fb
调用无参构造函数创建UserService:com.buydeem.factorybean.UserService@614ddd49
com.buydeem.factorybean.UserService@614ddd49
com.buydeem.factorybean.UserServiceFactoryBean@627551fb
打印的结果你一定很意外,因为我们的配置文件中我们注册的userService它的class为com.buydeem.factorybean.UserServiceFactoryBean,而我们通过getBean("userService")得到的却是UserService类型,而不是UserServiceBean类型。
因为FactoryBean是一个特殊的Bean。我们自定义的UserServiceFactoryBean实现了FactoryBean接口,该类型会在容器中注册两个Bean,其中一个为UserServiceFactoryBean另一个为getObject()方法返回的对象。而且我们通过BeanName获取的为UserService而不是UserServiceFactoryBean,如果想要获取UserServiceFactoryBean,只需要在名字前添加&即可。
在使用Mybatis时我们通常需要创建SqlSessionFactory实例,该实例创建过程较为复杂。而在使用Mybatis与Spring整合时,我们可以使用SqlSessionFactoryBean来创建SqlSessionFactory。
spring中相关源码
AbstractBeanFactory中doGetBean源码如下:
![](https://img.haomeiwen.com/i6743136/d00d16d969012db1.png)
doGetBean方法内容较多,我只截取了一段。该段代码为创建单例Bean时的主要代码,它首先创建sharedInstance这个实例Bean,接着调用getObjectForBeanInstance获取真正的实例。运行我们之前的示例代码,打断点调试可以知道,sharedInstance实际上就是UserServiceFactoryBean实例,而bean才是UserService实例。
![](https://img.haomeiwen.com/i6743136/bc9218ccbef708ac.png)
getObjectFromFactoryBean该方法的核心就是判断之前创建的sharedInstance是不是FactoryBean,如果是则获取真正的bean。
网友评论