BeanFactory和FactoryBean区别(含源码分析)

1.BeanFactory

BeanFactory是Spring中的一个接口,是一个工厂类,用于管理Bean。BeanFactory是IOC容器的核心,指责包括:实例化,配置应用程序中的对象和建立这些对象之间的依赖关系,ApplicationContext则是BeanFactory的高级版本。

XMLBeanFactory是BeanFactory的一种实现,通过读取XML元数据中的信息,来实例化Bean,交由Spring容器管理,构建一个可配置的应用。

BeaFactory中有如下方法:

boolean containsBean(String beanName)

Object getBean(String)

Object getBean(String, Class)

Class getType(String name)

boolean isSingleton(String)

String[] getAliases(String name)

可以方便地获取bean,对Bean进行操作和判断。

2.FactoryBean则是一种可以定制实例化Bean的方式,通过实现FactoryBean,完成自定义的Bean实例化细节。例如可以通过FactoryBean代理对象,对其所有方法进行拦截,形成AOP类似功能。使用FactoryBean避免了在XML文件中配置各种属性,更灵活,但是需要在FactoryBean的实现类中编码完成类的实例化方式。

FactoryBean有如下方法:

public interface FactoryBean<T> {

   T getObject() throws Exception;

   Class<?> getObjectType();

   boolean isSingleton();

}

支持泛型,可将被实际对象传入,调用getObject方法时返回的是传入泛型的指定对象类型。

先用一个例子使用一下FactoryBean:

@Component("carFactoryBean")
public class CarFactoryBean implements FactoryBean<Car> {

    private String carInfo;

    public String getCarInfo() {
        return carInfo;
    }

    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }

    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        car.setBrand(carInfo.split(",")[0]);
        car.setName(carInfo.split(",")[1]);
        return car;
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

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

    public static void main(String[] args) {

    }
}

XML文件配置如下

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="carFactoryBean" class="com.fanfte.spring.CarFactoryBean" >
        <property name="carInfo" value="BMW,AAA"></property>
    </bean>

</beans>

在客户端中使用:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-test.xml");
        Car o1 = context.getBean("carFactoryBean", Car.class);
        Object o2 = context.getBean("&carFactoryBean");
        System.out.println(o1.getClass() + " " + o1.getName());
        System.out.println(o2.getClass());
    }
}

实际输出

class com.fanfte.spring.Car AAA
class com.fanfte.spring.CarFactoryBean

如果getBean传入参数位bean的名称,加上"&"则会返回FactoryBean对象本身。

接下来查看一下源码看看Spring中context.getBean("carFactoryBean", Car.class);做了什么

跟踪doGetBean可以到如下方法

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;

   // 在此获取到了carFactoryBean实例
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      // 此处获取到FactoryBean中的对象
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

在getObjectForBeanInstance中判断是否是FactoryBean对象,如果是,则通过它的getObject()获取对象方法构造对象。

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

   // 验证是否是FactoryBean对象,不是则抛出异常
   // 1.bean的name如果以&开头,则是FactoryBean实例,判断是否是FactoryBean的实例
   // 2.是否是FactoryBean的实例
   // 如果以&开头,但不是FactoryBean实例,则抛出异常
   if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
   }

   // 执行到此处,已经得到了bean,如果是普通的bean,或者是FacoryBean实例,直接返回
   // 是factoryBean实例,BeanFactoryUtils.isFactoryDereference则是以是否以&开头判断的
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }
   // 如果不是以&开头的FactoryBean,从FactoryBeanObjectCache中获取由Factory创建的bean
   Object object = null;
   if (mbd == null) {
      // 从FactoryBeanObjectCache中获取由Factory创建的bean
      object = getCachedObjectForFactoryBean(beanName);
   }
   // 没有在cache中获取到bean
   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的工作委托给getObjectFormFactoryBean方法
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

String FACTORY_BEAN_PREFIX = "&";
// 判断bean的name是否是factoryBean,判断是否以&开头
public static boolean isFactoryDereference(String name) {
   return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}

进入getObjectFromFactoryBean方法

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) {
            // 实际获取FactoryBean
            object = doGetObjectFromFactoryBean(factory, beanName);
            // Only post-process and store if not put there already during getObject() call above
            // (e.g. because of circular reference processing triggered by custom getBean calls)
            Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
            if (alreadyThere != null) {
               object = alreadyThere;
            }
            else {
               if (object != null && shouldPostProcess) {
                  try {
                     object = postProcessObjectFromFactoryBean(object, beanName);
                  }
                  catch (Throwable ex) {
                     throw new BeanCreationException(beanName,
                           "Post-processing of FactoryBean's singleton object failed", ex);
                  }
               }
               this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
            }
         }
         return (object != NULL_OBJECT ? object : null);
      }
   }
   else {
      Object object = doGetObjectFromFactoryBean(factory, beanName);
      if (object != null && shouldPostProcess) {
         try {
            object = postProcessObjectFromFactoryBean(object, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
         }
      }
      return object;
   }
}

进入获取FactoryBean中对象的方法

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(new PrivilegedExceptionAction<Object>() {
               @Override
               public Object run() throws Exception {
                     return factory.getObject();
                  }
               }, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         // 调用carFactoryBean的getObject()方法,进行自定义实例化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 && isSingletonCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(
            beanName, "FactoryBean which is currently in creation returned null from getObject");
   }
   return object;
}

最后进入carFactoryBean的getObject()方法。

@Override
public Car getObject() throws Exception {
    Car car = new Car();
    car.setBrand(carInfo.split(",")[0]);
    car.setName(carInfo.split(",")[1]);
    return car;
}

至此,自定义通过factoryBean获取对象完成。

总结:

1.BeanFactory是Spring的工厂接口,为IOC容器的核心实现。FactoryBean提供了方法自定义实现Bean,避免了复杂的配置,可用于实现对象代理等,封装了对象创建的内部实现,并提供了获取对象的接口。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容