Spring IOC(四)FactoryBean
Spring IOC(四)FactoryBean
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
一般情况下,Spring 通过反射机制利用 bean 的 class 属性指定实现类来实例化 bean。在某些情况下特别是整合第三方包时,实例化 bean 过程比较复杂,如果按照传统的方式,则需要在 XML 中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring 为此提供了一个 FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 bean 的逻辑。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
在该接口中还定义了以下3个方法:
(1) T getObject():返回由 FactoryBean 创建的 bean 实例,如果 isSingleton() 返回 true,则该实例会放到 Spring 容器中单实例存池中
(2) boolean isSingleton():返回由 FactoryBean 创建的 bean 实例的作用域是 singleton 还是 prototype
(3) Class<?> getObjectType():返回 FactoryBean 创建的 bean 类型。
一、Spring 中使用 FactoryBean
在如下的 Bean 通过 FactoryBean 注入
public class CarFactoryBean implements FactoryBean<Car> {
private String carInfo;
@Override
public Car getObject() throws Exception {
String[]infos = carInfo.split(",");
return Car car=new Car(infos[0], Integer.parseInt(infos[1]), Double.parseDouble(infos[2]));
}
@Override
public Class<?> getObjectType() {
return null;
}
}
public class Car {
private String brand;
private int maxSpeed;
private Double price;
// get/set
}
有了这个 CarFactoryBean 后,就可以在配置文件中使用下面这种自定义的配置方式配置了
<bean id="car" class="spring.factory_bean.CarFactoryBean">
<property name="carInfo" value="红旗CA72,200,20000.00"/>
</bean>
当调用 getBean("car") 时,Spring 通过反射机制发现 CarFactoryBean 实现了 FactoryBean 的接口,这时 Spring 容器就调用接口方法 CarFactoryBean#getObject() 方法返回。如果希望获取 CarFactoryBean 的实例,则需要在使用 getBean(beanName) 方法时在 beanName 前显示的加上 & 前缀,例如 getBean("&car")
二、FactoryBeanRegistrySupport
FactoryBeanRegistrySupport 提供了对 FactoryBean 的支持,最重要的方法是 getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess)
,这个方法通过 factoryBean 获取 bean 对象。
(1) 属性
// 缓存 beanName -> FactoryBean 的集合
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
(2) getObjectFromFactoryBean 注册
getObjectFromFactoryBean 负责从 FactoryBean#getObject() 中获取真正想要的 bean 对象,而不是 FactoryBean 本身。AbstractBeanFactory#getObjectForBeanInstance 获取 bean 之前会判断是不是 FactoryBean。
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) {
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)
// TODO ? 什么意思,循环引用?
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
} else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
beforeSingletonCreation(beanName);
try {
// 外面 BeanPostProcessor 作用在 factory 上,没有作用在实际想要的实例上,这边补一个
// 也就是说 BeanPostProcessor 的 postProcessBeforeInitialization 不会作用在 FactoryBean 上
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName", ex);
} finally {
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
// 调用 FactoryBean#getObject() 创建 bean
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object = factory.getObject();
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
// 子类重写
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
return object;
}
(3) 查找与删除
protected Object getCachedObjectForFactoryBean(String beanName) {
return this.factoryBeanObjectCache.get(beanName);
}
@Override
protected void removeSingleton(String beanName) {
synchronized (getSingletonMutex()) {
super.removeSingleton(beanName);
this.factoryBeanObjectCache.remove(beanName);
}
}
@Override
protected void clearSingletonCache() {
synchronized (getSingletonMutex()) {
super.clearSingletonCache();
this.factoryBeanObjectCache.clear();
}
}
(4) 其它方法
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
return factoryBean.getObjectType();
}
protected FactoryBean<?> getFactoryBean(String beanName, Object beanInstance) throws BeansException {
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanCreationException(beanName,
"Bean instance of type [" + beanInstance.getClass() + "] is not a FactoryBean");
}
return (FactoryBean<?>) beanInstance;
}
每天用心记录一点点。内容也许不重要,但习惯很重要!
Spring IOC(四)FactoryBean的更多相关文章
- Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段
目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...
- 【Spring IoC】依赖注入DI(四)
平常的Java开发中,程序员在某个类中需要依赖其它类的方法.通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖类不由程 ...
- Spring Ioc源码分析系列--容器实例化Bean的四种方法
Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...
- J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP
J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP 前言 搜狐畅游笔试题中有一道问答题涉及到回答谈谈对Spring IOC与AOP的理解.特将相关内容进行整理. ...
- Spring IOC(四)总结升华篇
本系列目录 Spring IOC(一)概览 Spring IOC(二)容器初始化 Spring IOC(三)依赖注入 Spring IOC(四)总结升华 =============正文分割线===== ...
- spring IOC中四种依赖注入方式
在spring ioc中有三种依赖注入,分别是:https://blog.csdn.net/u010800201/article/details/72674420 a.接口注入:b.setter方法注 ...
- 转:深入浅出spring IOC中四种依赖注入方式
转:https://blog.csdn.net/u010800201/article/details/72674420 深入浅出spring IOC中四种依赖注入方式 PS:前三种是我转载的,第四种是 ...
- 六、spring之通过FactoryBean为ioc容器中添加组件
前面我们已经介绍了几种为容器中添加组件的方法,今天一起学习通过FactoryBean添加组件的方法. 首先我们准备一个类,也就是我们需要注册进spring的ioc容器中的类 类Color: // 不必 ...
- spring(四):IoC
IoC-Inversion of Control,即控制反转 IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制. 理解IoC的关键:"谁控制谁,控制什么,为何是反 ...
随机推荐
- JDBC连接各种数据库的方法,连接MySql,Oracle数据库
JDBC连接各种数据库的方法: JDBC编程步骤: 1.导入jar包 2.注册驱动 3.获取数据库连接对象 4.定义SQL语句 5.获得执行SQL语句对象statemnet 6.执行SQL语句 7.处 ...
- NO_DATA_FOUND ORACL NVL函数,当第一个为空时显示第二个参数值
ORA-01403: no data foundORA-06512: at "STG.SAP_SO_QM_CUSTOMER_ADDBOM", line 50 NVL函数的格式如下: ...
- python 任务计划
sched 模块 引用time类实现任务定时执行 import time import sched def worker(msg): print msg s = sched.scheduler ...
- 解决ie浏览器下载apk或ipa变为zip
Tomcat/conf/web.xml <mime-mapping> <extension>apk</extension> <mime-type>app ...
- java面试题:数据库mysql
Web后端数据库一般用mysql. 数据库基础 Q:数据库三范式是什么? 第一范式:列不可再分 第二范式:行可以唯一区分,主键约束 第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束 且三大范 ...
- Redis集群部署及命令
一.简介 redis集群是一个无中心的分布式Redis存储架构,可以在多个节点之间进行数据共享,解决了Redis高可用.可扩展等问题. redis集群提供了以下两个好处: 将数据自动切分(split) ...
- Nginx 功能
本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得,欢迎留言交流. Nginx能做什么 ...
- Floyd算法简介
参考:https://blog.csdn.net/qq_35644234/article/details/60875818 一.Floyd算法的介绍 1.算法的特点: 弗洛伊德算法是解决任 ...
- CentOS 下lvm 磁盘扩容
打算给系统装一个oracle,发现磁盘空间不足.在安装系统的时候我选择的是自动分区,系统就会自动以LVM的方式分区.为了保证系统后期的可用性,建议所有新系统安装都采用LVM,之后生产上的设备我也打算这 ...
- 100. Same Tree (Tree;DFS)
Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...