Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?
ApplicationContext 接口,它由 BeanFactory 接口派生而来。
ApplicationContext 包含 BeanFactory 的所有功能,通常建议比 BeanFactory 优先。
BeanFactory 和 FactoryBean 的区别 BeanFactory 是接口,提供了 OC 容器最基本的形式,给具体的 IOC 容器的实现提供了规范,FactoryBean 也是接口,为 IOC 容器中 Bean 的实现提供了更加灵活的方式,FactoryBean 在 IOC 容器的基础上给 Bean 的实现加上了一个简单工厂模式和装饰模式(如果想了解装饰模式参考:修饰者模式(装饰者模式,Decoration) 我们可以在 getObject()方法中灵活配置。其实在 Spring 源码中有很多 FactoryBean 的实现类.
区别:BeanFactory 是个 Factory,也就是 IOC 容器或对象工厂,FactoryBean 是个 Bean。在 Spring 中,所有的 Bean 都是由 BeanFactory(也就是 IOC 容器)来进行管理的。
但对 FactoryBean 而言,这个 Bean 不是简单的 Bean,而是一个能生产或者修饰对象生成的工厂 Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
1、BeanFactoryBeanFactory,以 Factory 结尾,表示它是一个工厂类(接口), 它负责生产和管理 bean 的一个工厂。在 Spring 中,BeanFactory 是 IOC 容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
BeanFactory 只是个接口,并不是 IOC 容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext 等,其中 XmlBeanFactory 就是常用的一个,该实现将以 XML 方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory 类将持有此 XML 配置元数据,并用它来构建一个完全可配置的系统或应用。
都是附加了某种功能的实现。它为其他具体的 IOC 容器提供了最基本的规范,例如 DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext 等具体的容器都是实现了 BeanFactory,再在其基础之上附加了其他的功能。
BeanFactory 和 ApplicationContext 就是 spring 框架的两个 IOC 容器,现在一般使用 ApplicationnContext,其不但包含了 BeanFactory 的作用,同时还进行更多的扩展。
BeanFacotry 是 spring 中比较原始的 Factory。如 XMLBeanFactory 就是一种典型的 BeanFactory。
原始的 BeanFactory 无法支持 spring 的许多插件,如 AOP 功能、Web 应用等。ApplicationContext 接口,它由 BeanFactory 接口派生而来。
ApplicationContext 包含 BeanFactory 的所有功能,通常建议比 BeanFactory 优先
ApplicationContext 以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext 包还提供了以下的功能:
MessageSource, 提供国际化的消息访问
资源访问,如 URL 和文件
事件传播
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层;
在不使用 spring 框架之前,我们的 service 层中要使用 dao 层的对象,不得不在 service 层中 new 一个对象。
存在的问题:层与层之间的依赖。service 层要用 dao 层对象需要配置到 xml 配置文件中,至于对象是怎么创建的,关系是怎么组合的都交给了 spring 框架去实现。
方式 1、
Resource resource = new FileSystemResource("beans.xml");BeanFactory factory = new XmlBeanFactory(resource);方式 2、
ClassPathResource resource = new ClassPathResource("beans.xml");BeanFactory factory = new XmlBeanFactory(resource);方式 3、
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});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 名称的所有别名
package org.springframework.beans.factory;import org.springframework.beans.BeansException;public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;Class<?> getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);}
2、FactoryBean 一般情况下,Spring 通过反射机制利用的 class 属性指定实现类实例化 Bean,在某些情况下,实例化 Bean 过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。
Spring 为此提供了一个 org.springframework.bean.factory.FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑。bean 为什么默认单例?推荐看下。关注微信公众号:Java 技术栈,在后台回复:spring,可以获取我整理的 N 篇 Spring 教程,都是干货。
FactoryBean 接口对于 Spring 框架来说占用重要的地位,Spring 自身就提供了 70 多个 FactoryBean 的实现。它们隐藏了实例化一些复杂 Bean 的细节,给上层应用带来了便利。
从 Spring3.0 开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean 的形式以 Bean 结尾,表示它是一个 Bean,不同于普通 Bean 的是:它是实现了 FactoryBean 接口的 Bean,根据该 Bean 的 ID 从 BeanFactory 中获取的实际上是 FactoryBean 的 getObject()返回的对象,而不是 FactoryBean 本身,如果要获取 FactoryBean 对象,请在 id 前面加一个 &符号来获取。
例如自己实现一个 FactoryBean,功能:用来代理一个对象,对该对象的所有方法做一个拦截,在调用前后都输出一行 LOG,模仿 ProxyFactoryBean 的功能。
/**
my factory bean<p>
代理一个类,拦截该类的所有方法,在方法的调用前后进行日志的输出
@author daniel.zhao
*/public class MyFactoryBean implements FactoryBean<Object>, InitializingBean, DisposableBean {
}XML-Bean 配置如下
<bean id="fbHelloWorldService" class="com.ebao.xxx.MyFactoryBean"><property name="interfaceName" value="com.ebao.xxx.HelloWorldService" /><property name="target" ref="helloWorldService" /></bean>Junit Test class
@RunWith(JUnit4ClassRunner.class)@ContextConfiguration(classes = { MyFactoryBeanConfig.class })public class MyFactoryBeanTest {@Autowiredprivate ApplicationContext context;/*** 测试验证 FactoryBean 原理,代理一个 servcie 在调用其方法的前后,打印日志亦可作其他处理* 从 ApplicationContext 中获取自定义的 FactoryBean* context.getBean(String beanName) ---> 最终获取到的 Object 是 FactoryBean.getObejct(),* 使用 Proxy.newInstance 生成 service 的代理类*/@Testpublic void testFactoryBean() {HelloWorldService helloWorldService = (HelloWorldService) context.getBean("fbHelloWorldService");helloWorldService.getBeanName();helloWorldService.sayHello();}}FactoryBean 是一个接口,当在 IOC 容器中的 Bean 实现了 FactoryBean 后,通过 getBean(String BeanName)获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject()方法返回的对象。要想获取 FactoryBean 的实现类,就要 getBean(&BeanName),在 BeanName 之前加上 &。
package org.springframework.beans.factory;public interface FactoryBean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton();}在该接口中还定义了以下 3 个方法:
TgetObject():返回由 FactoryBean 创建的 Bean 实例,如果 isSingleton()返回 true,则该实例会放到 Spring 容器中单实例缓存池中;
booleanisSingleton():返回由 FactoryBean 创建的 Bean 实例的作用域是 singleton 还是 prototype;
ClassgetObjectType():返回 FactoryBean 创建的 Bean 类型。
当配置文件中的 class 属性配置的实现类是 FactoryBean 时,通过 getBean()方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject()方法所返回的对象,相当于 FactoryBean#getObject()代理了 getBean()方法。例:如果使用传统方式配置下面 Car 的时,Car 的每个属性分别对应一个元素标签。
package com.baobaotao.factorybean;public class Car {private int maxSpeed ;private String brand ;private double price ;public int getMaxSpeed () {return this . maxSpeed ;}public void setMaxSpeed ( int maxSpeed ) {this . maxSpeed = maxSpeed;}public String getBrand () {return this . brand ;}public void setBrand ( String brand ) {this . brand = brand;}public double getPrice () {return this . price ;}public void setPrice ( double price ) {this . price = price;}}如果用 FactoryBean 的方式实现就灵活点,下例通过逗号分割符的方式一次性的为 Car 的所有属性指定配置值:
package com.baobaotao.factorybean;import org.springframework.beans.factory.FactoryBean;public class CarFactoryBean implements FactoryBean<Car> {private String carInfo ;public Car getObject () throws Exception {Car car = new Car () ;String [] infos = carInfo .split ( "," ) ;car.setBrand ( infos [ 0 ]) ;car.setMaxSpeed ( Integer. valueOf ( infos [ 1 ])) ;car.setPrice ( Double. valueOf ( infos [ 2 ])) ;return car;}public Class<Car> getObjectType () {return Car. class;}public boolean isSingleton () {return false ;}public String getCarInfo () {return this . carInfo ;}
}有了这个 CarFactoryBean 后,就可以在配置文件中使用下面这种自定义的配置方式配置 CarBean 了:
<bean d="car"class="com.baobaotao.factorybean.CarFactoryBean"P:carInfo="法拉利,400,2000000"/>当调用 getBean(“car”)时,Spring 通过反射机制发现 CarFactoryBean 实现了 FactoryBean 的接口,这时 Spring 容器就调用接口方法 CarFactoryBean#getObject()方法返回。
如果希望获取 CarFactoryBean 的实例,则需要在使用 getBean(beanName)方法时在 beanName 前显示的加上”&”前缀:如 getBean(“&car”);
下面是一个应用 FactoryBean 的例子
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="student" class="com.spring.bean.Student"><property name="name" value="zhangsan" /></bean>
<bean id="school" class="com.spring.bean.School"></bean>
<bean id="factoryBeanPojo" class="com.spring.bean.FactoryBeanPojo"><property name="type" value="student" /></bean></beans>FactoryBean 的实现类
import org.springframework.beans.factory.FactoryBean;
/**
@author 作者 wangbiao
@parameter
@return*/public class FactoryBeanPojo implements FactoryBean{private String type;
@Overridepublic Object getObject() throws Exception {if("student".equals(type)){return new Student();}else{return new School();}
}
@Overridepublic Class getObjectType() {return School.class;}
@Overridepublic boolean isSingleton() {return true;}
public String getType() {return type;}
public void setType(String type) {this.type = type;}
}普通的 bean
/**
@author 作者 wangbiao
@parameter
@return*/public class School {private String schoolName;private String address;private int studentNumber;public String getSchoolName() {return schoolName;}public void setSchoolName(String schoolName) {this.schoolName = schoolName;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public int getStudentNumber() {return studentNumber;}public void setStudentNumber(int studentNumber) {this.studentNumber = studentNumber;}@Overridepublic String toString() {return "School [schoolName=" + schoolName + ", address=" + address+ ", studentNumber=" + studentNumber + "]";}}测试类
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.spring.bean.FactoryBeanPojo;
/**
@author 作者 wangbiao
@parameter
@return*/public class FactoryBeanTest {public static void main(String[] args){String url = "com/spring/config/BeanConfig.xml";ClassPathXmlApplicationContext cpxa = new ClassPathXmlApplicationContext(url);Object school= cpxa.getBean("factoryBeanPojo");FactoryBeanPojo factoryBeanPojo= (FactoryBeanPojo) cpxa.getBean("&factoryBeanPojo");System.out.println(school.getClass().getName());System.out.println(factoryBeanPojo.getClass().getName());}}输出的结果:
十一月 16, 2016 10:28:24 上午 org.springframework.context.support.AbstractApplicationContext prepareRefreshINFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1e8ee5c0: startup date [Wed Nov 16 10:28:24 CST 2016]; root of context hierarchy 十一月 16, 2016 10:28:24 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitionsINFO: Loading XML bean definitions from class path resource [com/spring/config/BeanConfig.xml]十一月 16, 2016 10:28:24 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletonsINFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@35b793ee: defining beans [student,school,factoryBeanPojo]; root of factory hierarchycom.spring.bean.Studentcom.spring.bean.FactoryBeanPojo 从结果上可以看到当从 IOC 容器中获取 FactoryBeanPojo 对象的时候,用 getBean(String BeanName)获取的确是 Student 对象,可以看到在 FactoryBeanPojo 中的 type 属性设置为 student 的时候,会在 getObject()方法中返回 Student 对象。
所以说从 IOC 容器获取实现了 FactoryBean 的实现类时,返回的却是实现类中的 getObject 方法返回的对象,要想获取 FactoryBean 的实现类,得在 getBean(String BeanName)中的 BeanName 之前加上 &,写成 getBean(String &BeanName)。
最后如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑
如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star: https://gitee.com/ZhongBangKeJi/crmeb_java不胜感激 !
JAVA 学习手册:https://doc.crmeb.com技术交流论坛:https://q.crmeb.com
评论