源码分析FactoryBean接口不为人知的秘密

FactoryBean接口在spring中是一个非常好用且容易被人忽略的接口。OK借助这个机会我们把它的秘密扒出来,让它落体出镜好吧。
首先贴一个代码:

import org.springframework.beans.factory.FactoryBean;

public class FactoryBeanTest implements FactoryBean {

    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Object getObject() throws Exception {
        return new UserServiceImpl();
    }

    @Override
    public Class getObjectType() {
        return userService.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

这个代码很简单,弄一个类实现了FactoryBean接口,而接口中有三个必须实现的方法:
getObject、getObjectType、isSingleton
在getObject中我们new UserServiceImpl();实例。然后我们在配置文件中是这样实例化FactoryBeanTest 类的:

 

那么我们通过spring的上下文根据id,id="factoryBeanTest"获取FactoryBeanTest的实例。

ApplicationContext applicationContext = ApplicationContextAwareTest.getApplicationContext();
UserServiceImpl userService = (UserServiceImpl)applicationContext.getBean("factoryBeanTest");
System.out.println(userService);

OK,我们看看打印的对象是什么:

com.xiangxueedu.carry.factoryBean.UserServiceImpl@5038d0b5

从结果我们可以看到,打印出来的是FactoryBeanTest类中的getObject方法的返回对象。这是实现了FactoryBean接口的通常用法。根据Id获取到的是getObject的方法值对象。

OK,有的时候我们需要根据Id获取到类本身怎么办,
比如:
要根据id=“factoryBeanTest”,获取到FactoryBeanTest这个类本身,怎么呢??
有办法,我们只要用上下文对象的getBean方法中加一个“&”就行了。如下:

ApplicationContext applicationContext = ApplicationContextAwareTest.getApplicationContext();
FactoryBeanTest factoryBeanTest = (FactoryBeanTest)applicationContext.getBean("&factoryBeanTest");
System.out.println(factoryBeanTest);

打印结果如下:

com.xiangxueedu.carry.factoryBean.FactoryBeanTest@5038d0b5

我们用“&factoryBeanTest”的方式成功的拿到了FactoryBeanTest类的实例。

根据两种情况我们分析一下源码:
**

1、没有“&”号为什么根据Id获取到的是getObject方法的返回对象?

源码分析FactoryBean接口不为人知的秘密_第1张图片
这个是bean在实例化过程中FactoryBean接口的调用入口。我们进去看看

	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		if (BeanFactoryUtils.isFactoryDereference(name) && !(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());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

其实加“&”和不加“&”就是影响了一个if的判断,导致了代码往不往下执行
源码分析FactoryBean接口不为人知的秘密_第2张图片
我们进去看看BeanFactoryUtils.isFactoryDereference(name),第一个条件!(beanInstance instanceof FactoryBean)如果是factorybean类型的bean正在实例化,那么肯定是false的。

public static boolean isFactoryDereference(String name) {
	return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}

String FACTORY_BEAN_PREFIX = “&”;
这里就判断了id中有没有存在“&”符号,如果有则返回true,返回true就直接return beanInstance;把实例本身返回了,而实例本身就是例子中的FactoryBeanTest。

如果id中没有“&”,则返回false,那么代码往下执行。
源码分析FactoryBean接口不为人知的秘密_第3张图片
会执行到这个方法。

源码分析FactoryBean接口不为人知的秘密_第4张图片
源码分析FactoryBean接口不为人知的秘密_第5张图片
这里就调到了getObject方法,所以拿到的对象就是getObject方法返回的对象。OK。

从分析源码我们可以看到,就是一个“&”符号控制了代码的流转,有“&”拿到的是类本身实例,没有“&”拿到的getObject返回实例。满意的同学请点个赞呗。

你可能感兴趣的:(FactoryBean,spring源码,&,spring源码)