Spring源码学习(五) 创建Bean过程中的扩展点

Easter79
• 阅读 474

引言

上班挺累
事事都烦
写篇文章
兑现诺言

一图胜所有

Spring源码学习(五) 创建Bean过程中的扩展点
绿色的部一般用于Spring内部扩展,黄色的部分可用于自定义实例化。 本文仅仅聊聊InitializingBean,对于绿色部分,建议您查看,其他人写的blog https://my.oschina.net/xiaolyuh/blog/3113215

InitializingBean接口

接口注释

/**
 * 1. 实现的接口是个Bean,BeanFactory设置它所有的属性后触发。
 * 2. 可用于执行自定义实例化或校验必要的属性是否被设置。
 * 注:实现InitializingBean的另外一种方式是,
 *    制定一个自定义的init method,
 *    通过<bean> 元素的 init-method或者,使用@PostConstruct注解
*/
public interface InitializingBean {
   void afterPropertiesSet() throws Exception;
}

应用举例

找具体例子时,我鬼使神差的找到了mybatis与Spring结合的时候用到的SqlSessionFactoryBean类,实现如下:

public class SqlSessionFactoryBean implements 
FactoryBean<SqlSessionFactory>, 
InitializingBean, ApplicationListener<ApplicationEvent> {
    public void afterPropertiesSet() throws Exception {
      notNull(dataSource, "Property 'dataSource' is required");
      notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
      state((configuration == null && configLocation == null) 
      || !(configuration != null && configLocation != null),
  "Property 'configuration' and 'configLocation' can not specified with together");
  this.sqlSessionFactory = buildSqlSessionFactory();
}
    
   //省略其他实现 ...
   }

afterPropertiesSet的实现很简单,就是创建了SqlSessionFactory。比起afterPropertiesSet,FactoryBean接口更令我好奇,待我仔细阅读了FactoryBean的注释,发现上边的图少了一个重要扩展点。

另一个重要扩展FactoryBean

解决实例化Bean过程比较复杂的问题,可以实现该FactoryBean接口定制实例化Bean的逻辑.
尽管FactoryBean以bean的风格定义,但是它总是对外暴露getObject()创建的对象。 -- 温安适总结于 20191013

FactoryBean的编程契约如下

  1. 它的实现不应该依赖注解注入或者其他反射工具
  2. getObjectType(),getObject()的调用早于服务启动,甚至早于任何 post-processor
  3. 如果你需要获取其他的Bean,你需要实现BeanFactoryAware接口,并手动编程获取其他bean。
  4. 最后FactoryBean对象参与BeanFactory的同步创建bean的过程,通常不需要内部同步,除了懒加载FactoryBean本身时。

注释翻译

/**
 *  1. 实现这个接口的是个bean,与BeanFactory结合使用。
 *  2. 它自己是单个对象的工厂。
 *  3.FactoryBean 支持单例和原形,并能按需提供懒加载或启动时提前暴露。
 *  4. SmartFactoryBean允许暴露更多细粒度行为元数据
 *  5.Spring框架本身大量使用这个接口,
 *   例如{@link org.springframework.aop.framework.ProxyFactoryBean},
 * {@link org.springframework.jndi.JndiObjectFactoryBean}。
 */
public interface FactoryBean<T> {

   /**
    * 返回一个被当前工厂管理的实例(可能共享或独立)
    * 与BeanFactory结合,支持单例,原形模式。
    * 如果FactoryBean在被调用时没有完全的加载(例如存在循环引用)
    * 将抛出FactoryBeanNotInitializedException。
    * Spring 2.0以后,FactoryBeans允许返回null,
    * 返回null时,不在抛出FactoryBeanNotInitializedException,
    * FactoryBean的实现会视情况而定,
    *是否抛出FactoryBeanNotInitializedException
    * BeanFactory的实现必须考虑这种情况.
    */
   @Nullable
   T getObject() throws Exception;

   /**
    * 返回FactoryBean创建对象的类型,如果事先不知道返回null
    *允许在实例化对象时检查特定类型的bean(例如用于自动装配时)。
    *在创建单例对象的实现时,该方法应尽量避免单例创建,它应该提前估计类型。
    *在创建原形类型的object时,也建议返回有意义的类型信息。
    *这个方法,可以在FactoryBean完全实例化之前调用。它不能依赖于初始化。
    *注意:自动注入将简单忽略factoryBean,这个方法会返回null。
    *因此,强烈建议使用factorybean的当前状态正确地实现此方法。
    */
   @Nullable
   Class<?> getObjectType();

   /**
    * 返回true时, getobject()
    * 将始终返回相同的对象。
    * 注意:如果一个factorybean保存一个单例对象,
    * 从 getObject()返回的对象
    * 可能被拥有它的BeanFaFactory缓存。
    * factorybean本身的单例状态通常是由拥有它的BeanFactory确定;
    */
   default boolean isSingleton() {
      return true;
   }

}

BeanFactory与FactoryBean区别

接口名称

BeanFactory

FactoryBean

用途

BeanFactory是IOC容器的顶层接口。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖

解决实例化Bean过程比较复杂的问题。通过实现该接口定制实例化Bean的逻辑

管理的对象

所有bean

getObject方法创建的对象

点赞
收藏
评论区
推荐文章
浅梦一笑 浅梦一笑
2个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
技术小男生 技术小男生
2个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi /etc/profile2:按字母键i进入编辑模式,在最底部添加内容: JAVAHOME/opt/jdk1.8.0152 CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jar PATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
2个月前
Java面向对象试题
1、 请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。 创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现 接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿 吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
2个月前
css问题
1、 在IOS中图片不显示(给图片加了圆角或者img没有父级) <div<img src""/</div div {width: 20px; height: 20px; borderradius: 20px; overflow: h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录 问题 用navicat导入数据时,报错: 原因这是因为当前的MySQL不支持datetime为0的情况。 解决修改sql\mode: sql\mode:SQL Mode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。 全局s
晴空闲云 晴空闲云
2个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。 盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
1个月前
快速入门|使用MemFire Cloud构建React Native应用程序
> MemFire Cloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Easter79 Easter79
1年前
swap空间的增减方法
(1)增大swap空间 去激活swap交换区: #swapoff -v /dev/vg00/lvswap 扩展交换lv: #lvextend -L 10G /dev/vg00/lvswap 重新生成swap交换区: #mkswap /dev/vg00/lvswap 激活新生成的交换区: #swapon -v /dev/vg00/lvswap
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
#### 背景描述 # Time: 2019-01-24T00:08:14.705724+08:00 # User@Host: **[**] @ [**] Id: ** # Schema: sentrymeta Last_errno: 0 Killed: 0 # Query_time: 0.315758 Lock_
helloworld_28799839 helloworld_28799839
2个月前
常用知识整理
# Javascript ## 判断对象是否为空 ```js Object.keys(myObject).length === 0 ``` ## 经常使用的三元运算 > 我们经常遇到处理表格列状态字段如 `status` 的时候可以用到 ``` vue
helloworld_34035044 helloworld_34035044
4个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。 uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid() 或 uuid(sep)参数说明:sep 布尔值,生成的uuid中是否包含分隔符'',缺省为