1、概述
Spring中有两种类型的Bean:一种是普通的JavaBean;另一种就是工厂Bean(FactoryBean),这两种Bean都受Spring的IoC容器管理,但它们之间却有一些区别。
2、详述
普通的JavaBean不再多说,我们将其定义好,然后在配置文件中定义并配置其依赖关系,就可以通过IoC容器的getBean获取到。
那么FactoryBean呢?
FactoryBean跟普通Bean不同,通过BeanFactory类的getBean方法直接获取到的并不是该FactoryBean的实例,而是该FactoryBean中方法getObject返回的对象。但我们可以通过其它途径获取到该FactoryBean的实例,方法就是在通过getBean方法获取实例时在参数name前面加上“&”符号即可,比如com.zyh.spring3.hello.MyFactoryBean类是一个工厂Bean,其定义如下:
- <bean id="myFactoryBean" class="com.zyh.spring3.hello.MyFactoryBean"></bean>
如果我们使用beanFactory.getBean("myFactoryBean")获取Bean实例,获取到的将是MyFactoryBean类中定义的getObject方法返回的对象;但是如果我们使用beanFactory.getBean("&myFactoryBean")方法获取到的却是MyFactoryBean类的实例。
这个获取工厂Bean实例的前缀符号定义在BeanFactory接口中,如下所示:
- package org.springframework.beans.factory;
-
- import org.springframework.beans.BeansException;
-
- public interface BeanFactory {
-
-
-
-
-
-
-
- String FACTORY_BEAN_PREFIX = "&";
-
- Object getBean(String name) throws BeansException;
-
-
- }
自定义FactoryBean需要实现Spring定义的FactoryBean接口。
3、示例
下面以一个小例子来验证。
1、定义一个FactoryBean
- package com.zyh.spring3.hello;
-
- import java.util.Date;
-
- import org.springframework.beans.factory.BeanNameAware;
- import org.springframework.beans.factory.FactoryBean;
-
- public class MyFactoryBean implements FactoryBean<Date>,BeanNameAware {
- private String name;
-
- @Override
- public Date getObject() throws Exception {
- return new Date();
- }
-
- @Override
- public Class<?> getObjectType() {
- return Date.class;
- }
-
- @Override
- public boolean isSingleton() {
- return false;
- }
-
- public void sayName() {
- System.out.println("My name is " + this.name);
- }
-
- @Override
- public void setBeanName(String name) {
- this.name = name;
- }
- }
2、在XML文件中配置
- <bean id="myFactoryBean" class="com.zyh.spring3.hello.MyFactoryBean"></bean>
3、写个测试方法
- @Test
- public void testFactoryBean() {
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
- Resource resource = new ClassPathResource("helloworld.xml");
- XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
- reader.loadBeanDefinitions(resource);
-
- Date now = (Date) beanFactory.getBean("myFactoryBean");
- System.out.println(now);
- MyFactoryBean factoryBean = (MyFactoryBean) beanFactory.getBean("&myFactoryBean");
- factoryBean.sayName();
- }
则可以看到程序正常,且输出内容为:
- Tue Oct 23 18:37:29 CST 2012
- My name is myFactoryBean
4、源码分析
下面从源码的角度分析一下FactoryBean的实现原理。
首先看一下默认IoC容器DefaultListableBeanFactory的继承关系图和FactoryBean的getObject方法最终被调用的方法调用关系图:
继承关系:
调用关系:
其实FactoryBean对于IoC容器来说与普通的JavaBean也是没什么区别的,同样在容器的管理之下,配置上也没什么区别,唯一不同的就是FactoryBean需要实现Spring定义的FactoryBean接口,并实现其中的getObject方法。既然没什么不同,那也就说明FactoryBean实例其实也是在IoC容器中,只不过我们在获取目标Bean的时候需要判断是返回FactoryBean本身还是调用其getObject方法,将其返回对象返回,这里用作判断的依据就是&符号。
1、getBean方法
我们通过getBean(String name)方法获取Bean的时候,其实调用的是抽象IoC容器AbstractBeanFacotry中的getBean方法,源码如下(去掉了一些无关紧要的代码):
- public Object getBean(String name) throws BeansException {
- return doGetBean(name, null, null, false);
- }
-
- protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
- throws BeansException {
-
- final String beanName = transformedBeanName(name);
- Object bean;
-
-
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- if (logger.isDebugEnabled()) {
-
- }
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- } else {
-
-
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- } catch (BeansException ex) {
-
- }
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- } else if (mbd.isPrototype()) {
-
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- } finally {
- afterPrototypeCreation(beanName);
- }
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- } else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- } finally {
- afterPrototypeCreation(beanName);
- }
- }
- });
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- } catch (IllegalStateException ex) {
-
- }
- }
- }
-
- return (T) bean;
- }
上面的代码中,首先调用了一个方法transformedBeanName,并以name为参数,其实这个方法中又调用了另外的方法最终调用到BeanFactoryUtils中的工具方法transformedBeanName,这个方法的作用是将判断name是否含有&符号,如果有则将其去掉并将结果返回。由此可见,即使我们传入带有&符号的name最终一样会将其去掉,&不过是个标志。
- protected String transformedBeanName(String name) {
- return canonicalName(BeanFactoryUtils.transformedBeanName(name));
- }
-
-
- public static String transformedBeanName(String name) {
- Assert.notNull(name, "'name' must not be null");
- String beanName = name;
- while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
- beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
- }
- return beanName;
- }
对参数name处理后,程序拿着处理结果(可能是去掉&符号或未作处理的name,此时改称beanName)去IoC容器查找目标Bean(也可能是新创建Bean实例),得到的Bean则有可能是普通的JavaBean也可能是FactoryBean,然后进入到下一步处理。
2、getObjectForBeanInstance方法
由上面两段代码可以看到,不论是何种情况,程序最终会调用到getObjectForBeanInstance这个方法,而这个方法同样是定义在AbstractBeanFactory这个抽象容器类中:
- protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
-
-
- if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
- throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
- }
-
-
-
-
-
- if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
- return beanInstance;
- }
-
- Object object = null;
- if (mbd == null) {
- object = getCachedObjectForFactoryBean(beanName);
- }
- if (object == null) {
-
- FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
-
- if (mbd == null && containsBeanDefinition(beanName)) {
- mbd = getMergedLocalBeanDefinition(beanName);
- }
- boolean synthetic = (mbd != null && mbd.isSynthetic());
-
- object = getObjectFromFactoryBean(factory, beanName, !synthetic);
- }
- return object;
- }
上面这段代码显示,如果判断当前得到的Bean不是FactoryBean或者通过getBean方法传进来的name是以&开头,则直接返回当前得到的Bean。也就是说如果我们以getBean("&beanName")方法来获取FactoryBean的实例,在此就直接返回了,其中BeanFactoryUtils.isFactoryDereference(name)方法用来判断我们传入的name是否以&符号开头,该方法定义如下:
- public static boolean isFactoryDereference(String name) {
- return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
- }
如果该Bean是FactoryBean实例并且我们的目标不是获取FactoryBean的实例,而是要获取该FactoryBean通过getObject方法生成的对象,则继续往下处理,程序进入到getObjectFromFactoryBean(factory, beanName, !synthetic)方法。
3、getObjectFromFactoryBean方法
getObjectFromFactoryBean方法定义在IoC容器的父类FactoryBeanRegistrySupport中,在该方法执行过程中,随后又调用了同类中的doGetObjectFromFactoryBean这个具体处理方法,其定义如下:
- protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
- if (factory.isSingleton() && containsSingleton(beanName)) {
- synchronized (getSingletonMutex()) {
- Object object = this.factoryBeanObjectCache.get(beanName);
- if (object == null) {
- object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
- this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
- }
- return (object != NULL_OBJECT ? object : null);
- }
- }
- else {
- return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
- }
- }
-
- private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
- throws BeanCreationException {
- Object object;
- try {
- if (System.getSecurityManager() != null) {
- AccessControlContext acc = getAccessControlContext();
- try {
- object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return factory.getObject();
- }
- }, acc);
- } catch (PrivilegedActionException pae) {
- throw pae.getException();
- }
- } else {
- object = factory.getObject();
- }
- }
-
- return object;
- }
这里返回的已经是作为工厂的FactoryBean生产的产品了,而不再是FactoryBean本身了。
4、源码分析总结
从上面几段Spring源码分析可以得知,Spring首先对传入getBean方法的参数name作处理,如果带有&符号,则将其去掉,否则什么也不做。然后通过处理后的name得到Bean的实例(从缓存中获取或者直接创建),该实例可能是普通的JavaBean实例也可能是FactoryBean实例,此时我们会分析处理前的name是否有&符号及该Bean是否为FactoryBean,有以下可能的处理分支:
A、如果处理前的name带有&前缀(表示要取FactoryBean的实例而非其getObject方法返回的对象)或者当前得到的实例并非FactoryBean实例,则在得到Bean实例后直接返回;
B、剩下的情况就是处理前的name不带有&符号,并且当前取得的Bean是FactoryBean实例,那么去调用该FactoryBean的getObject方法,并将得到的对象返回。
转自:http://blog.csdn.net/zhyh1986/article/details/8104051