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;

 基本就是这些了,接着使用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
  1. <bean id="personBean" class="com.gh.MavenTest.Person">  
  2.     <property name="name" value="gh1" />  
  3.     <property name="address" value="address1" />  
  4.     <property name="age" value="28" />  
  5. bean>  
那么现在我们可以通过getBean("personBean")来获取该对象。那么我们来看下如果通过实现FactoryBean以后该怎么写呢?来看下我们的PersonFactoryBean的代码:
[java]  view plain  copy
  1. public class PersonFactoryBean implements FactoryBean{  
  2.       
  3.     private String personInfo;  
  4.       
  5.     public Person getObject() throws Exception {  
  6.         Person person =  new  Person () ;      
  7.         String []  infos =  personInfo.split ( "," ) ;  
  8.         person.setName(infos[0]);  
  9.         person.setAddress(infos[1]);  
  10.         person.setAge(Integer.parseInt(infos[2]));  
  11.         return person;  
  12.     }  
  13.   
  14.     public Class getObjectType() {  
  15.         return Person.class;  
  16.     }  
  17.   
  18.     public boolean isSingleton() {  
  19.         return true;  
  20.     }  
  21. }  
我们看到,这里PersonFactoryBean实现了FactoryBean接口,那么自然也要实现它定义的方法。这里我们是通过一个personInfo字符串解析得到Person对象,那么我们在配置Spring的时候就可以这么配置:
[html]  view plain  copy
  1. <bean id="personFactory" class="com.hik.MavenTest.PersonFactory">  
  2.     <property name="personInfo" value="gh2,address2,22">property>  
  3. bean>   
OK,那么这个时候我们getBean("personFactory")得到的就是Person对象而不是PersonFactoryBean对象。具体原理参考上面在IOC的应用,我们通过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)这个方法,具体调用到了getObject方法,所以结果很明显。

应用案例

很多开源项目在集成Spring 时都使用到FactoryBean,比如 MyBatis3 提供 mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean

<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="trade" />
        <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
    bean>

org.mybatis.spring.SqlSessionFactoryBean 如下:


package org.mybatis.spring;

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
        ......
}

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

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    ">
     
    
    <dubbo:application name="demo-consumer" />
   
   
    <dubbo:protocol name="dubbo" port="20813" />    

    
    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService"  />
     
beans>

对应的Bean是com.alibaba.dubbo.config.spring.ReferenceBean 类,如下:

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {

}


作者:FX_SKY
链接:https://www.jianshu.com/p/6f0a59623090
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • FactoryBean:是一个Java Bean,但是它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像。比如下:我们把bean比作是人,那么FactoryBean可以算是一个女人,首先它本身也是一个人,但它能够生产人。【挺尴尬的比喻】。
  • BeanFactory:这就是一个Factory,是一个IOC容器或者叫对象工厂,它里面存着很多的bean。还用上面那个比如:如果bean是人,那么它可以理解成学校,学校里面很多人,学校管理这么多的人。

你可能感兴趣的:(Spring BeanFactory与FactoryBean的区别)