Spring FactoryBean的源码分析

前言

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前加&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值