Spring扩展点之FactoryBean接口
前言
首先看一下接口定义
public interface FactoryBean<T> {
/**
* 返回对象实例
*/
@Nullable
T getObject() throws Exception;
/**
* 返回对象类型,
*/
@Nullable
Class<?> getObjectType();
/**
* 该工厂管理的对象是否为单例?
*/
default boolean isSingleton() {
return true;
}
}
由接口定义可以看出来,实现这个接口的bean不是主要功能,getObject()
创建的对象才是重点。那么在这我们就可以猜到了,可以是使用FactoryBean
创建一些实例化过程比较复杂的bean
FactoryBean
的注册
FactoryBean
的处理逻辑在AbstractBeanFactory.doGetBean
方法内
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//获取bean名称
final String beanName = transformedBeanName(name);
Object bean;
//省略部分内容
//这里就是FactoryBean的相关处理,下面会展开说
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
//省略部分内容
return (T) bean;
}
看一下具体的逻辑,这里需要注意Spring关于bean的name有个潜规则,凡是以&开头的bean名称都默认为FactoryBean
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 如果beanName以工厂引用&开头
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// 如果name以&开头,而beanInstance不是FactoryBean类型,则抛异常
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
}
// 如果beanInstance不是FactoryBean类型,则直接返回beanInstance
// 或者name以&开头,也直接返回beanInstance,说明我们就想获取FactoryBean实例
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 此时beanInstance是FactoryBean类型,而name又不是以&开头; 这是我们示例工程的情况,也是最普通、用的最多的情况
// 将beanInstance强转成FactoryBean类型
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 从缓存中获取我们需要的实例对象
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 调用FactoryBean的getObject方法创建我们需要的实例对象
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
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) {
//通过factory.getObject获取
object = doGetObjectFromFactoryBean(factory, beanName);
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 {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
//将获取到的对象放到factoryBeanObjectCache单例缓存map进行存储
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
//非单例的处理,直接通过factory.getObejct获取,然后再返回给用户
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;
}
}
生成bean对象的方法:
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
object = factory.getObject();//生成对象
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
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;}
Spring的实现
Spring中实现这个接口的bean有很多,但是我们最熟悉也是最重要的就是在我之前文章中提到过得ProxyFactoryBean
这个bean是实现AOP技术的重点,简单回顾一下吧
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
Spring扩展点之FactoryBean接口的更多相关文章
- Spring扩展点之Aware接口族
引言 Spring中提供了各种Aware接口,方便从上下文中获取当前的运行环境,比较常见的几个子接口有:BeanFactoryAware,BeanNameAware,ApplicationContex ...
- Spring扩展点-v5.3.9
Spring 扩展点 **本人博客网站 **IT小神 www.itxiaoshen.com 官网地址****:https://spring.io/projects/spring-framework T ...
- Spring之BeanFactory和FactoryBean接口的区别
目录 一.BeanFactory接口 二.FactoryBean接口 1.简单实现 2.增强实现 3.FactoryBean的实际使用案例 三.总结 @ Spring框架中的BeanFactory ...
- Spring扩展点之BeanPostProcessor
前言 BeanPostProcessor接口是Spring中一个非常重要的接口,它的接口定义如下 public interface BeanPostProcessor { Object postPro ...
- Spring扩展点之BeanFactoryPostProcessor
前言 BeanFactoryPostProcessor接口是Spring中一个非常重要的接口,它的接口定义如下 public interface BeanFactoryPostProcessor { ...
- spring扩展点之PropertyPlaceholderConfigurer
原理机制讲解 https://leokongwq.github.io/2016/12/28/spring-PropertyPlaceholderConfigurer.html 使用时多个配置讲解 ht ...
- spring mvc 提供的几个常用的扩展点
转载 :http://blog.csdn.net/gufachongyang02/article/details/43836105 这是spring3 mvc的核心流程图: SpirngMVC的第 ...
- Spring钩子方法和钩子接口的使用详解
本文转自:http://www.sohu.com/a/166804449_714863 前言 SpringFramework其实具有很高的扩展性,只是很少人喜欢挖掘那些扩展点,而且官方的Refrenc ...
- Spring中的扩展点
Spring作为一个常用的IOC框架,在设计上预留了很多的扩展点,很多第三方开源框架,包括Spring自身也是基于这些扩展点实现的,这很好的体现了对修改关闭.对扩展开放的原则.总的来说Spring的扩 ...
随机推荐
- html 后手
1.a (1.)超链接 <a href="new.html">点击进入到新网页</a>这里可以直接跳转到一个页面 <a href="http ...
- constant timer(固定定时器),constant throughput timer(常数吞吐量定时器);多个请求,某个请求a下,设置常数吞吐量定时器,模式:all active threads(shared)则所有请求吞吐量一致;
1.两请求之间添加'固定定时器' 1000ms,那么两请求发送间隔时间是多少? 1000ms吗? 由实验得出,2个请求发送间隔时间 = 1000ms + 第一个请求时间(发出至完成后时间) 2.单个请 ...
- c# 第五节 第一个控制台程序、第一个桌面、快捷键、注释
本节内容: 1:控制台程序的创建 2:第一个桌面程序 3:快捷键 4:注释 一.第一个控制台程序: 这就是控制台程序: 打开你的vs2015,按如下操作 二.第一个桌面程序 比如当我们删除一个东西会弹 ...
- 201871010105-曹玉中《面向对象程序设计(java)》第十五周学习总结
201871010105-曹玉中<面向对象程序设计(java)>第十五周学习总结 项目 内容 这个作业属于哪个过程 https://www.cnblogs.com/nwnu-daizh/ ...
- 201871010134-周英杰《面向对象程序设计(java)》第十四周学习总结
项目 内容 这个作业属于哪个课程 <https://home.cnblogs.com/u/nwnu-daizh/> 这个作业的要求在哪里 <https://www.cnblogs.c ...
- 201871010136-赵艳强《面向对象程序设计(java)》第十四周学习总结
201871010136-赵艳强<面向对象程序设计(java)>第十四周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...
- 使用 Nacos 的 Docker 镜像,启动 Nacos 服务
1.镜像网址:https://hub.docker.com/r/nacos/nacos-server 2.Clone project git clone --depth 1 https://githu ...
- Visual Assist X(网上收集,仅供学习与研究,支持正版)
Visual AssistX是一款非常好的Microsoft Visual Studio插件,它可以完全集成到您的Microsoft开发环境中,升级了您的IDE, 在不改变编程习惯的同时就可以感受到V ...
- 【day05】css
一.盒模型(BoxModel) 1.width 宽度 2.height 高度 说明: 块元素和有宽高属性的标记(img,input) 能设置宽度和高度,而行元素不能设置宽高 3 ...
- Ubuntu放弃战斗, Linux桌面的悲哀 - 简书
Ubuntu放弃战斗, Linux桌面的悲哀 - 简书 https://www.jianshu.com/p/86dd6e34ce91