Spring中的FactoryBean

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,其定义如下:

[html]  view plain copy
  1. <bean id="myFactoryBean" class="com.zyh.spring3.hello.MyFactoryBean"></bean>  

如果我们使用beanFactory.getBean("myFactoryBean")获取Bean实例,获取到的将是MyFactoryBean类中定义的getObject方法返回的对象;但是如果我们使用beanFactory.getBean("&myFactoryBean")方法获取到的却是MyFactoryBean类的实例。

这个获取工厂Bean实例的前缀符号定义在BeanFactory接口中,如下所示:

[java]  view plain copy
  1. package org.springframework.beans.factory;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4.   
  5. public interface BeanFactory {  
  6.   
  7.     /** 
  8.      * Used to dereference a {@link FactoryBean} instance and distinguish it from 
  9.      * beans <i>created</i> by the FactoryBean. For example, if the bean named 
  10.      * <code>myJndiObject</code> is a FactoryBean, getting <code>&myJndiObject</code> 
  11.      * will return the factory, not the instance returned by the factory. 
  12.      */  
  13.     String FACTORY_BEAN_PREFIX = "&";  
  14.   
  15.     Object getBean(String name) throws BeansException;  
  16.   
  17.     // 省略其它一些代码  
  18. }  

自定义FactoryBean需要实现Spring定义的FactoryBean接口。


3、示例

下面以一个小例子来验证。

1、定义一个FactoryBean

[java]  view plain copy
  1. package com.zyh.spring3.hello;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.springframework.beans.factory.BeanNameAware;  
  6. import org.springframework.beans.factory.FactoryBean;  
  7.   
  8. public class MyFactoryBean implements FactoryBean<Date>,BeanNameAware {  
  9.     private String name;  
  10.   
  11.     @Override  
  12.     public Date getObject() throws Exception {  
  13.         return new Date();  
  14.     }  
  15.   
  16.     @Override  
  17.     public Class<?> getObjectType() {  
  18.         return Date.class;  
  19.     }  
  20.   
  21.     @Override  
  22.     public boolean isSingleton() {  
  23.         return false;  
  24.     }  
  25.       
  26.     public void sayName() {  
  27.         System.out.println("My name is " + this.name);  
  28.     }  
  29.   
  30.     @Override  
  31.     public void setBeanName(String name) {  
  32.         this.name = name;  
  33.     }  
  34. }  

2、在XML文件中配置

[html]  view plain copy
  1. <bean id="myFactoryBean" class="com.zyh.spring3.hello.MyFactoryBean"></bean>  

3、写个测试方法

[java]  view plain copy
  1. @Test  
  2. public void testFactoryBean() {  
  3.     DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();  
  4.     beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());  
  5.     Resource resource = new ClassPathResource("helloworld.xml");  
  6.     XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);  
  7.     reader.loadBeanDefinitions(resource);  
  8.           
  9.     Date now = (Date) beanFactory.getBean("myFactoryBean");  
  10.     System.out.println(now);  
  11.     MyFactoryBean factoryBean = (MyFactoryBean) beanFactory.getBean("&myFactoryBean");  
  12.     factoryBean.sayName();  
  13. }  
则可以看到程序正常,且输出内容为:

[java]  view plain copy
  1. Tue Oct 23 18:37:29 CST 2012  
  2. My name is myFactoryBean  

4、源码分析

下面从源码的角度分析一下FactoryBean的实现原理。

首先看一下默认IoC容器DefaultListableBeanFactory的继承关系图和FactoryBean的getObject方法最终被调用的方法调用关系图:

继承关系:

Spring中的FactoryBean_第1张图片

调用关系:

Spring中的FactoryBean_第2张图片

其实FactoryBean对于IoC容器来说与普通的JavaBean也是没什么区别的,同样在容器的管理之下,配置上也没什么区别,唯一不同的就是FactoryBean需要实现Spring定义的FactoryBean接口,并实现其中的getObject方法。既然没什么不同,那也就说明FactoryBean实例其实也是在IoC容器中,只不过我们在获取目标Bean的时候需要判断是返回FactoryBean本身还是调用其getObject方法,将其返回对象返回,这里用作判断的依据就是&符号。


1、getBean方法

我们通过getBean(String name)方法获取Bean的时候,其实调用的是抽象IoC容器AbstractBeanFacotry中的getBean方法,源码如下(去掉了一些无关紧要的代码):

[java]  view plain copy
  1. public Object getBean(String name) throws BeansException {  
  2.     return doGetBean(name, nullnullfalse);  
  3. }  
  4.   
  5. protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)  
  6.             throws BeansException {  
  7.   
  8.     final String beanName = transformedBeanName(name);  
  9.     Object bean;  
  10.   
  11.     // Eagerly check singleton cache for manually registered singletons.  
  12.     Object sharedInstance = getSingleton(beanName);  
  13.     if (sharedInstance != null && args == null) {  
  14.         if (logger.isDebugEnabled()) {  
  15.             // 省略日志代码  
  16.         }  
  17.         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  
  18.     } else {  
  19.         // 省略部分代码  
  20.         // Create bean instance.  
  21.         if (mbd.isSingleton()) {  
  22.             sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {  
  23.                 public Object getObject() throws BeansException {  
  24.                     try {  
  25.                         return createBean(beanName, mbd, args);  
  26.                     } catch (BeansException ex) {  
  27.                         // catch code  
  28.                     }  
  29.                 }  
  30.             });  
  31.             bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);  
  32.         } else if (mbd.isPrototype()) {  
  33.             // It's a prototype -> create a new instance.  
  34.             Object prototypeInstance = null;  
  35.             try {  
  36.                 beforePrototypeCreation(beanName);  
  37.                 prototypeInstance = createBean(beanName, mbd, args);  
  38.             } finally {  
  39.                 afterPrototypeCreation(beanName);  
  40.             }  
  41.             bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);  
  42.         } else {  
  43.             String scopeName = mbd.getScope();  
  44.             final Scope scope = this.scopes.get(scopeName);  
  45.             if (scope == null) {  
  46.                 throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");  
  47.             }  
  48.             try {  
  49.                 Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {  
  50.                     public Object getObject() throws BeansException {  
  51.                         beforePrototypeCreation(beanName);  
  52.                         try {  
  53.                             return createBean(beanName, mbd, args);  
  54.                         } finally {  
  55.                             afterPrototypeCreation(beanName);  
  56.                         }  
  57.                     }  
  58.                 });  
  59.                 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);  
  60.             } catch (IllegalStateException ex) {  
  61.                 // 省略部分代码  
  62.             }  
  63.         }  
  64.     }  
  65.     // 省略部分代码  
  66.     return (T) bean;  
  67. }  

上面的代码中,首先调用了一个方法transformedBeanName,并以name为参数,其实这个方法中又调用了另外的方法最终调用到BeanFactoryUtils中的工具方法transformedBeanName,这个方法的作用是将判断name是否含有&符号,如果有则将其去掉并将结果返回。由此可见,即使我们传入带有&符号的name最终一样会将其去掉,&不过是个标志。

[java]  view plain copy
  1. protected String transformedBeanName(String name) {  
  2.     return canonicalName(BeanFactoryUtils.transformedBeanName(name));  
  3. }  
  4.   
  5. // BeanFactoryUtils中的工具方法transformedBeanName  
  6. public static String transformedBeanName(String name) {  
  7.     Assert.notNull(name, "'name' must not be null");  
  8.     String beanName = name;  
  9.     while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {  
  10.         beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());  
  11.     }  
  12.     return beanName;  
  13. }  

对参数name处理后,程序拿着处理结果(可能是去掉&符号或未作处理的name,此时改称beanName)去IoC容器查找目标Bean(也可能是新创建Bean实例),得到的Bean则有可能是普通的JavaBean也可能是FactoryBean,然后进入到下一步处理。


2、getObjectForBeanInstance方法

由上面两段代码可以看到,不论是何种情况,程序最终会调用到getObjectForBeanInstance这个方法,而这个方法同样是定义在AbstractBeanFactory这个抽象容器类中:

[java]  view plain copy
  1. protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {  
  2.   
  3.     // Don't let calling code try to dereference the factory if the bean isn't a factory.  
  4.     if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {  
  5.         throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());  
  6.     }  
  7.   
  8.     // Now we have the bean instance, which may be a normal bean or a FactoryBean.  
  9.     // If it's a FactoryBean, we use it to create a bean instance, unless the  
  10.     // caller actually wants a reference to the factory.  
  11.     // 如果这里不是对FactoryBean的调用(不是FactoryBean或者beanName是以&开头表示取FactoryBean实例),则结束处理,直接返回  
  12.     if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {  
  13.         return beanInstance;  
  14.     }  
  15.   
  16.     Object object = null;  
  17.     if (mbd == null) {  
  18.         object = getCachedObjectForFactoryBean(beanName);  
  19.     }  
  20.     if (object == null) {  
  21.         // Return bean instance from factory.  
  22.         FactoryBean<?> factory = (FactoryBean<?>) beanInstance;  
  23.         // Caches object obtained from FactoryBean if it is a singleton.  
  24.         if (mbd == null && containsBeanDefinition(beanName)) {  
  25.             mbd = getMergedLocalBeanDefinition(beanName);  
  26.         }  
  27.         boolean synthetic = (mbd != null && mbd.isSynthetic());  
  28.         // 这里从FactoryBean中得到最终的Bean  
  29.         object = getObjectFromFactoryBean(factory, beanName, !synthetic);  
  30.     }  
  31.     return object;  
  32. }  
上面这段代码显示,如果判断当前得到的Bean不是FactoryBean或者通过getBean方法传进来的name是以&开头,则直接返回当前得到的Bean。也就是说如果我们以getBean("&beanName")方法来获取FactoryBean的实例,在此就直接返回了,其中BeanFactoryUtils.isFactoryDereference(name)方法用来判断我们传入的name是否以&符号开头,该方法定义如下:

[java]  view plain copy
  1. public static boolean isFactoryDereference(String name) {  
  2.     return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));  
  3. }  

如果该Bean是FactoryBean实例并且我们的目标不是获取FactoryBean的实例,而是要获取该FactoryBean通过getObject方法生成的对象,则继续往下处理,程序进入到getObjectFromFactoryBean(factory, beanName, !synthetic)方法。


3、getObjectFromFactoryBean方法

getObjectFromFactoryBean方法定义在IoC容器的父类FactoryBeanRegistrySupport中,在该方法执行过程中,随后又调用了同类中的doGetObjectFromFactoryBean这个具体处理方法,其定义如下:

[java]  view plain copy
  1. protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {  
  2.     if (factory.isSingleton() && containsSingleton(beanName)) {  
  3.         synchronized (getSingletonMutex()) {  
  4.             Object object = this.factoryBeanObjectCache.get(beanName);  
  5.             if (object == null) {  
  6.                 object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
  7.                 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));  
  8.             }  
  9.             return (object != NULL_OBJECT ? object : null);  
  10.         }  
  11.     }  
  12.     else {  
  13.         return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
  14.     }  
  15. }  
  16.   
  17. private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName, final boolean shouldPostProcess)  
  18.             throws BeanCreationException {  
  19.     Object object;  
  20.     try {  
  21.         if (System.getSecurityManager() != null) {  
  22.             AccessControlContext acc = getAccessControlContext();  
  23.             try {  
  24.                 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
  25.                     public Object run() throws Exception {  
  26.                             return factory.getObject();  
  27.                         }  
  28.                     }, acc);  
  29.             } catch (PrivilegedActionException pae) {  
  30.                 throw pae.getException();  
  31.             }  
  32.         } else {  
  33.             object = factory.getObject();   // 这里调用的就是FactoryBean接口中的getObject方法  
  34.         }  
  35.     }  
  36.     // 省略部分catch语句及其后续处理语句  
  37.     return object;  
  38. }  

这里返回的已经是作为工厂的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

你可能感兴趣的:(spring)