Spring bean加载之1:BeanFactory和FactoryBean
BeanFactory
BeanFactory:以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。
FactoryBean的使用
一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.Springframework.bean.factory.FactoryBean的工程类接口,用户可以通过实现接口定制实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式:
Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
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定义是否为单例模式。对于Singleton属性,可以在BeanDefinition中指定。
- String[] getAliases(String name) 返回给定bean名称的所有别名,这些别名都是用户在BeanDefinition中指定的。
- isPrototype(String)查询指定名字的Bean是否是prototype类型的。与Singleton属性一样,这个属性也可以在BeanDefinition中指定。
- isTypeMatch()查询指定名字的Bean的Class类型是否是特定的Class类型。这个Class类型可以由用户来指定。
这些定义的接口方法勾画了IoC容器的基本特性,因为BeanFactory接口定义了IoC容器。

FactoryBean
FactoryBean:以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
FactoryBean
下面展示了FactoryBean提供的接口方法,需要注意的是,在Spring中为我们实现了大量的FactoryBean,所以可以看出FactoryBean是非常重要的。
FactoryBean的使用
一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.Springframework.bean.factory.FactoryBean的工程类接口,用户可以通过实现接口定制实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式:

package org.springframework.beans.factory;
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
- Object getObject():返回本工厂创建的对象实例。此实例也许是共享的,依赖于该工厂返回的是单例或者是原型。
- boolean isSingleton():如果FactoryBean返回的是单例,该方法返回值为true,否则为false。
- Class getObjectType():返回对象类型。对象类型是getObject()方法返回的对象的类型,如果不知道的类型则返回null。
FactoryBean概念和接口在Spring框架中大量使用。Spring内置的有超过70个实现。
当配置文件中<bean>的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getBean()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
例如:如果使用传统方式配置下面Car的<bean>时,Car的每个属性分别对应一个<property>元素标签。
public class Car{
private int maxSpeed;
private String brand;
private double price;
//get set方法
}
如果用FactoryBean的方式实现就会灵活一些,下例通过逗号分割符的方式一次性地为Car的所有属性指定配置值:
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;
}
public void setCarInfo(Stirng carInfo){
this.carInfo = carInfo;
}
}
有了这个CarFactoryBean后,就可以在配置文件中使用下面这种自定义的配置方式配置CarBean了:
<bean id="car" class="com.test.factorybean.CarFactoryBean" carInfo="超级跑车,400,100000" />
当调用getBean("car")时,Spring通过反射机制发现CarBeanFactory实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回。如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀,例如getBean("&car")。
应用场景
FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。
应用案例
如果大家有看过Mybatis的SessionFactoryBean和Activiti初始化引擎的ProcessEngineFactoryBean两个类就应该了解FactoryBean的具体使用。
很多开源项目在集成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);
......
}
FactoryBean的功能貌似更像是一种代理,有一种场景是,我们使用一个通用的类来在xml文件中注册bean,我们希望通过该通用bean产生一个我们希望的bean,而这个需求FactoryBean就可以办到,你只需要拦截你需要代理的bean,然后转换成你希望的bean再注册。一个应用场景就是Rpc服务器端的bean注册,以及Rpc客户端的服务调用,都可以通过一个第三方bean来产生我们真正需要的bean。
当xml的配置文件中<bean>的class属性配置的实现类是FactoryBean时(如下面配置文件"bean id="car"),通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean.getObject()方法所返回的对象,相当于FactoryBean.getObject()代理了getBean()方法。例如:如果使用传统方式配置下面Car的<bean>时,Car的每个属性分别对应一个<property>元素标签。
public class Car {
private int maxSpeed ;
private String brand ;
private double price ;
//get/set方法
}
对应Car在xml中的配置:
<bean id="car" class="com.dxz.demo.Car">
<property name="maxSpeed">
<value>100</value>
</property>
<property name="brand">
<value>红旗</value>
</property>
<property name="price">
<value>999999</value>
</property>
</bean>
如果用FactoryBean的方式实现就会灵活一些,下例通过逗号分割符的方式一次性地为Car的所有属性指定配置值:
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 ;
}
// 接受逗号分割符设置属性信息
public void setCarInfo( String carInfo ) {
this.carInfocarInfo = carInfo;
}
}
有了这个CarFactoryBean后,就可以在配置文件中使用下面这种自定义的配置方式配置Car Bean了:
<bean id="car" class="com.dxz.demo.CarFactoryBean" carInfo="超级跑车,400,2000000"/>
当调用getBean("car") 时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean.getObject()方法返回。如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName) 方法时在beanName前显示的加上 "&" 前缀,例如getBean("&car")。(当使用ApplicationContext的getBean()方法获取FactoryBean实例本身而不是它所产生的bean,则要使用&符号+id。比如,现有FactoryBean,它有id,在容器上调用getBean("myBean")将返回FactoryBean所产生的bean,调用getBean("&myBean")将返回FactoryBean它本身的实例。)
Spring bean加载之1:BeanFactory和FactoryBean的更多相关文章
- Spring bean加载2--FactoryBean情况处理
Spring bean加载2--FactoryBean情况处理 在Spring bean加载过程中,每次bean实例在返回前都会调用getObjectForBeanInstance来处理Factory ...
- 07.Spring Bean 加载 - BeanDefinitionReader
基本概念 BeanDefinitionReader ,该接口的作用就是加载 Bean. 在 Spring 中,Bean 一般来说都在配置文件中定义.而在配置的路径由在 web.xml 中定义.所以加载 ...
- spring bean加载顺序指定方式之一
在某些情况下,我们在容器启动的时候做一些事情,举个例子,加载缓存等.. 此时我们会希望某个bean先被加载并执行其中的afterpropertiesset方法. 因为spring默认是通过contex ...
- Spring bean加载多个配置文件
除了写很简单的加载一个xml,加载多个的情况一直没用到,在公司里也不会由自己处理这个问题,现在需要用到了,就研究验证一下. 使用的案例还是上面的例子. 只有,将原来的beans.xml分成两个部分. ...
- spring bean 加载过程(spring)
以classpathXmlApplication为例 入口方法包含3个部分, public ClassPathXmlApplicationContext(String[] configLocation ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- 【Spring源码分析】Bean加载流程概览(转)
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
- 工厂模式模拟Spring的bean加载过程
一.前言 在日常的开发过程,经常使用或碰到的设计模式有代理.工厂.单例.反射模式等等.下面就对工厂模式模拟spring的bean加载过程进行解析,如果对工厂模式不熟悉的,具体可以先去学习一下工厂 ...
随机推荐
- Class T泛型和通配符泛型的区别
平时看java源代码的时候,如果碰到泛型的话,我想? T K V E这些是经常出现的,但是有时想不起来代表什么意思,今天整理下: ? 表示不确定的java类型. T 表示java类型. K V 分别代 ...
- docker 随笔记录
.docker 固定网络ip地址,启动 Docker的时候,用 --network 参数,可以指定网络类型 eg:docker run -itd --name test1 --network brid ...
- 第一步,怎么打开react的大门?
前言 其实我的react的实战经验很少,大概是17年-18年写了一个react全家桶的后台管理系统.猜测这个项目应该还一直在使用 在我手里的vue项目就比较多了,技术栈一直是vue全家桶.最近也在深入 ...
- 在Ubuntu 18.04 下安装mysql,没有初始密码,重设root密码
在Ubuntu 18.04 下安装mysql 不知道是由于mysql更新为新版还是.Ubuntu18.04中的特性,安装过程中没有设置密码的环节,在网络上找了半天,总算解决了!特此记录下来,以便以后查 ...
- 自定义alert弹框,title不显示域名(重写alert)
问题: 系统默认的alert弹框的title会默认显示网页域名 解决办法: (修改弹框样式) (function() { window.alert = function(name) { $(" ...
- textEdit
textEdit可以添加背景图片.渐变色.文字颜色.大小等等 <?xml version="1.0" encoding="utf-8"?> < ...
- 生成model笔记
https://github.com/yscacaca/DeepSense/tree/master/android_test这个才是真正的部署代码,跑这个代码就好. 跑python sample_mo ...
- 安装DEDECMS出现Function ereg_replace()错误的解决方法
这个问题我记得以前有遇到的,是在本地调试dedecms整站程序的时候,从网上下载了一个整套程序包括模板到本地调试,导入数据库安装都正常,但是登陆 后台就出现"Deprecated: Func ...
- Spring cloud微服务安全实战-4-8Zuul网关安全开发(一)
安全相关的代码和业务逻辑相关的代码实际上是在一个应用里面的,在这个应用里面,我们需要去,这个应用本身的处理逻辑里面需要去处理令牌和用户信息之间的转换. 然后我们需要去知道认证服务器的地址,这些都是耦合 ...
- Bmp格式图片与16进制的互相转换简解 Python
BMP TO HEX 首先介绍Github上一个简单的Bmp转成16进制的py: https://github.com/robertgallup/bmp2hex 网上这种例子很多.思路也简单:将bmp ...