Spring|Spring BeanFactory与FactoryBean的区别

1. BeanFactory BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。
实例化容器

1 Resource resource = new FileSystemResource("beans.xml"); 2 BeanFactory factory = new XmlBeanFactory(resource);

1 ClassPathResource resource = new ClassPathResource("beans.xml"); 2 BeanFactory factory = new XmlBeanFactory(resource);

1 ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"}); 3 BeanFactory factory = (BeanFactory) context;

【Spring|Spring BeanFactory与FactoryBean的区别】 基本就是这些了,接着使用getBean(String beanName)方法就可以取得bean的实例;BeanFactory提供的方法及其简单,仅提供了六种方法供客户调用:
  • boolean containsBean(String beanName) 判断工厂中是否包含给定名称的bean定义,若有则返回true
  • Object getBean(String) 返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
  • Object getBean(String, Class) 返回以给定名称注册的bean实例,并转换为给定class类型
  • Class getType(String name) 返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
  • boolean isSingleton(String) 判断给定名称的bean定义是否为单例模式

  • String[] getAliases(String name) 返回给定bean名称的所有别名


BeanFactory实例化Bean的过程:
1、 调用bean的默认构造方法(或指定的构造方法),生成bean1。
2、 将配置文件中配置的属性值注入bean1,生成bean2。
3、 如bean实现了InitalizingBean接口,则执行afterPropertiesSet()方法,生成bean3。
4、 如配置文件配置了init-method属性,则执行指定的方法,生成bean4。
构造函数-->set-->InitalizingBean#afterPropertiesSet()-->init-method



2, FactoryBean
以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

通常来讲,根据XML的节点中配置的class路径,Spring可以通过反射机制来实例化。
如:

但实际工作中,实例化一个对象并不如此简单。比如要在实例化的时候做一些复杂的处理。这就用上FactoryBean了。
比如最常用的Quartz框架,我们在配置调度任务的时候,配置的都是FactoryBean。
FactoryBean隐藏了许多实例化的复杂细节,方便上层调用。
如果在中配置的是FactoryBean,那么getBean()返回的其实是FactoryBean#getObject()返回的对象。
如:
getBean("beanA")返回的其实是BeanAFactoryBean#getObject()返回的对象,至于是什么,就由BeanAFactoryBean来决定了。
要想返回BeanFactory的实例,应该getBean("&beanA")。

我们先来看下FactoryBean的接口定义: [java]view plain copy
  1. "white-space:pre">public interface FactoryBean {
  2. //返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。
  3. T getObject() throws Exception;
  4. //返回FactoryBean创建的bean类型。
  5. Class getObjectType();
  6. //返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
  7. boolean isSingleton();
  8. }
所以:FactoryBean的核心就在于通过getObject方法可以获取的是它所生产的对象,所以我们在Proxy创建代理对象的时候就比较方便。还有一些bean,如果通过配置的方式,会显得比较麻烦和复杂,那么这时候适当的采用编码方式在某些场合下还是挺不错的。 我们下面就通过一个简单的例子来体验下getObject方法【讲道理,这里实际意义不多,重在理解方法含义】 假如:我们有个Person对象,里面包含 name,address,age。set、get方法不写了 [java]view plain copy
  1. public class Person {
  2. private String name;
  3. private String address;
  4. private int age;
  5. }
那么如果我们要在Spring中配置该对象的话,需要这么配置: [html]view plain copy
那么现在我们可以通过getBean("personBean")来获取该对象。那么我们来看下如果通过实现FactoryBean以后该怎么写呢?来看下我们的PersonFactoryBean的代码: [java]view plain copy
  1. public class PersonFactoryBean implements FactoryBean{
  2. private String personInfo;
  3. public Person getObject() throws Exception {
  4. Person person =newPerson () ;
  5. String []infos =personInfo.split ( "," ) ;
  6. person.setName(infos[0]);
  7. person.setAddress(infos[1]);
  8. person.setAge(Integer.parseInt(infos[2]));
  9. return person;
  10. }
  11. public Class getObjectType() {
  12. return Person.class;
  13. }
  14. public boolean isSingleton() {
  15. return true;
  16. }
  17. }
我们看到,这里PersonFactoryBean实现了FactoryBean接口,那么自然也要实现它定义的方法。这里我们是通过一个personInfo字符串解析得到Person对象,那么我们在配置Spring的时候就可以这么配置: [html]view plain copy
OK,那么这个时候我们getBean("personFactory")得到的就是Person对象而不是PersonFactoryBean对象。具体原理参考上面在IOC的应用,我们通过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)这个方法,具体调用到了getObject方法,所以结果很明显。 应用案例
很多开源项目在集成Spring 时都使用到FactoryBean,比如 MyBatis3 提供 mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean

org.mybatis.spring.SqlSessionFactoryBean 如下:
package org.mybatis.spring; public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener { private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class); ...... }

另外,阿里开源的分布式服务框架 Dubbo 中的Consumer 也使用到了FactoryBean:

对应的Bean是com.alibaba.dubbo.config.spring.ReferenceBean 类,如下:
public class ReferenceBean extends ReferenceConfig implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {}



作者:FX_SKY
链接:https://www.jianshu.com/p/6f0a59623090
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • FactoryBean:是一个Java Bean,但是它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像。比如下:我们把bean比作是人,那么FactoryBean可以算是一个女人,首先它本身也是一个人,但它能够生产人。【挺尴尬的比喻】。
  • BeanFactory:这就是一个Factory,是一个IOC容器或者叫对象工厂,它里面存着很多的bean。还用上面那个比如:如果bean是人,那么它可以理解成学校,学校里面很多人,学校管理这么多的人。

    推荐阅读