前言
Spring的基础能力BeanFactory与FactoryBean,beanFactory定义了Bean创建的工厂;factoryBean定义了Bean如何创建。
最近做项目,发现部分自定义工厂bean,在spring容器中定义bean直接使用FactoryBean对象。虽然在实际使用的时候发现创建了实现FactoryBean的类的getObject方法的对象。
1.demo分析
创建一个spring的实例工程
pom依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
</dependencies>
定义一个基础bean
@Data
@ToString
public class SpringTestBean {
private String beanName = SpringTestBean.class.getName();
}
定义一个factoryBean
@Component("springTestBean")
public class SpringTestFactory implements FactoryBean<SpringTestBean> {
public SpringTestBean getObject() throws Exception {
return new SpringTestBean();
}
public Class<SpringTestBean> getObjectType() {
return SpringTestBean.class;
}
}
创建一个test方法
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestMain.class)
public class SpringFactoryTest {
@Autowired
private BeanFactory beanFactory;
@Test
public void testSpringTestFactory(){
System.out.println(beanFactory.getBean("springTestBean"));
}
}
运行test方法
结果如下
#......省略日志
SpringTestBean(beanName=com.feng.bean.SpringTestBean)
Disconnected from the target VM, address: '127.0.0.1:39757', transport: 'socket'
说明factorybean创建的是getObject的对象,那么什么情况下可以创建factoryBean的对象呢
2. 源码分析
分析源码
beanFactory.getBean("springTestBean")
断点进入后
可以看到我们获取到的对象是sharedInstance,是Factory对象,而不是我们需要的object对象。
进入核心方法
getObjectForBeanInstance(sharedInstance, name, beanName, null)
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.
//如果是factoryBean的对象,定义了Spring对factoryBean对象的定义
//以&开头的beanName才会创建factoryBean的对象,否则创建factoryBean的实现方法getObject的对象,在下面的代码会分析
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
}
// 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) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
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());
//如果是factoryBean对象就创建bean
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
判断是否factoryBean的对象,beanName存在且以&符号开头(看来Spring是想让我们将&开头的beanName才会创建factoryBean而不是getObject的对象)
String FACTORY_BEAN_PREFIX = "&";
public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
看上面的注释,如果不是factoryBean就直接返回bean,如果是就判断是否是创建factoryBean对象还是我们自定义创建的实体bean ,如果是创建factoryBean对象bean(即以&开头的beanName),就直接返回bean
跟踪
getObjectFromFactoryBean(factory, beanName, !synthetic)
继续跟踪
然后发现 object = factory.getObject();
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//factory获取bean
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
进入了我们自定义的方法中
那么如果我们要获取factoryBean对象本身怎么办,根据上面代码的分析,Spring定义以&符号开头的为创建factoryBean对象本身,而不是创建getObject的对象
3. 创建factoryBean本身的bean
根据上面的结论以&开头的factoryBean对象不会调用getObject方法,修改代码
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestMain.class)
public class SpringFactoryTest {
@Autowired
private BeanFactory beanFactory;
@Test
public void testSpringTestFactory(){
System.out.println(beanFactory.getBean("&springTestBean"));
}
}
切记是获取bean的时候beanName前加&,不是定义的beanName加&。无论是获取factoryBean对象还是使用factoryBean创建的bean,两者的bean均会在Spring容器中创建。
跟踪方法,在方法如下
总结
Spring创建bean有配置文件,注解(包括@Bean、@Component等),还有factoryBean,factoryBean可以自定义bean的创建过程,实现按照我们的需求创建bean。
Spring获取bean时,会判断是否factoryBean,不是就返回当前bean;如果是继续判断是否我们需要获取factoryBean(Spring定义beanName以&开头)还是factoryBean的getObject方法对象,不以&开头的beanName会调用factoryBean的getObject方法创建bean。
FactoryBean创建bean会在Spring容器创建factoryBean的具体实例bean(相当于多创建了一个bean),获取FactoryBean实例对象,需要在FactoryBean的beanName前加&