Spring源码分析——Bean实例化策略

Spring源码分析——Bean实例化策略

本篇文章讨论Spring源码中BeanFactory和BeanDefinition的设计实现,分析Spring如何创建单例Bean和如何使用三级缓存解决循环依赖。

对应Github源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/02-bean-instance-strategy

引言

在上一节中我们提到了Bean创建的核心是AbstractAutowireCapableBeanFactory类,其中我们提到了doCreateBean是创建Bean实例:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
    beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    ....
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
  instanceWrapper = this.createBeanInstance(beanName, mbd, args);
  ....
  this.addSingletonFactory(beanName, () -> {
      return this.getEarlyBeanReference(beanName, mbd, bean);
    });
  ....
}

从上面的代码中看出,创建Bean实例的核心是this.createBeanInstance()函数,接着看这个函数:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
  // Make sure bean class is actually resolved at this point.
  Class<?> beanClass = resolveBeanClass(mbd, beanName);

  if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName,
     "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
  }

  Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
  if (instanceSupplier != null) {
   return obtainFromSupplier(instanceSupplier, beanName);
  }

  if (mbd.getFactoryMethodName() != null) {
   return instantiateUsingFactoryMethod(beanName, mbd, args);
  }

  // Shortcut when re-creating the same bean...
  boolean resolved = false;
  boolean autowireNecessary = false;
  if (args == null) {
   synchronized (mbd.constructorArgumentLock) {
    if (mbd.resolvedConstructorOrFactoryMethod != null) {
     resolved = true;
     autowireNecessary = mbd.constructorArgumentsResolved;
    }
   }
  }
  if (resolved) {
   if (autowireNecessary) {
    return autowireConstructor(beanName, mbd, nullnull);
   }
   else {
    return instantiateBean(beanName, mbd);
   }
  }

  // Candidate constructors for autowiring?
  Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
  if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
   return autowireConstructor(beanName, mbd, ctors, args);
  }

  // Preferred constructors for default construction?
  ctors = mbd.getPreferredConstructors();
  if (ctors != null) {
   return autowireConstructor(beanName, mbd, ctors, null);
  }

  // No special handling: simply use no-arg constructor.
  return instantiateBean(beanName, mbd);
 }

上面其实核心主要涉及两种实例化方式:

  1. autowireConstructor:使用自动装配其构造函数实现实例化,例如使用@Autowire标记注入Bean
  2. instantiateBean:全面负责创建和初始化Bean实例,会选择合适的实例化策略等完成Bean初始化

注意:

Spring默认使用instantiateBean方式实例化Bean,当例如使用@Autowire方式注入bean将可能使用autowireConstructor方式注入bean;但是,这都不是决定性条件,Spring实例化一个Bean的时候会根据Bean的配置(构造函数、一些规则)等选择合适的方式去实例化Bean。

但是无论是使用哪种方式,都涉及一个调用:

 /**
  * Return the instantiation strategy to use for creating bean instances.
  */

 protected InstantiationStrategy getInstantiationStrategy() {
  return this.instantiationStrategy;
 }

选择一种合适的实例化策略(下面开始展开)。

扩展:Spring中装配一个Bean有多少种方式

  1. 构造函数注入;(注意Bean有默认的无参构造函数,但是使用如@AllArgsConstructor注解后则需要显示指定无参构造函数)
  2. Setter方法注入;
  3. 字段注入;(定义字段并使用如@Autowire注解注入)
  4. Java配置类;(使用@Configuration注解标记的配置类中,使用@Bean标记的函数将交由Spring装配)
  5. XML配置文件;(使用XML声明一个Bean)
  6. 自动装配;(使用例如@Autowire、@Qualifier、@Resource注解标记的字段或方法参数)

InstantiationStrategy

Spring源码分析——Bean实例化策略
img

在Spring源码中,主要有两种实例化策略:

  1. SimpleInstantiationStrategy:简单的Bean实例化方式,使用默认构造函数生成Bean实例
  2. CglibSubclassingInstantiationStrategy:使用Cglib实例化Bean(Spring默认方式),Cglib生成的bean实例是原始Bean的子类,Cglib会生成代理对象,用于AOP、事务处理等功能。

具体体现:

Spring源码分析——Bean实例化策略
image-20230419150409420

SimpleInstantiationStrategy

如下,SimpleInstantiationStrategy将使用构造函数创建Bean实例:

Spring源码分析——Bean实例化策略
image-20230419150646964
Spring源码分析——Bean实例化策略
image-20230419150948820

CglibSubclassingInstantiationStrategy

创建一个简单的测试类:

public class CglibTest {

 @Test
 public void test() {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(CglibProxy.class);
  enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
  CglibProxy impl = (CglibProxy) enhancer.create();
  impl.say1();
  impl.say2();
 }
}
class CglibProxy {
 public void say1() {
  System.out.println("this CglibProxy say1()");
 }
 public void say2() {
  System.out.println("this CglibProxy say2()");
 }
}

打印结果:

my.CglibProxy$$EnhancerByCGLIB$$365af6e0@57f51dec
this CglibProxy say1()
this CglibProxy say2()

代理技术

Spring源码分析——Bean实例化策略
image-20230419163622091

核心: 都是通过代理类生成被代理类的对象引用,调用方通过调用代理类去调用具体的接口实现,代理类本身不提供任何服务,但是借助代理类可以实现AOP、事务等扩展功能。

  1. JDK动态代理:基于Java中的java.lang.reflect.Proxy类,通过反射机制再运行时动态的创建代理对象;它只能代理接口类型的目标对象,并且要求目标对象至少实现一个接口。
  2. Cglib动态代理:基于Cglib库,通过继承目标对象并重写其方法来创建代理对象;它可以代理任何类型的目标对象,并且不需要目标对象实现接口。

Spring源码专栏

此专栏将从Spring源码角度整体分析Spring设计思路以及常见的 面试题

配套作者的手写Spring的 项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关Spring源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号: 程序员涂陌
  • QQ交流群:866685601


原文始发于微信公众号(程序员涂陌): Spring源码分析——Bean实例化策略

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145621.html

(0)
小半的头像小半
0 0

相关推荐

  • 大数据HDFS凭啥能存下百亿数据? 微信精选

    大数据HDFS凭啥能存下百亿数据?

    0 0139
    小半的头像 小半
    2022年12月21日
  • Golang实现自己的Redis(Redis网络协议篇 Go语言

    Golang实现自己的Redis(Redis网络协议篇

    0 0119
    小半的头像 小半
    2024年3月2日
  • 快速了解JavaScript的选择框操作 前端开发

    快速了解JavaScript的选择框操作

    0 094
    小半的头像 小半
    2022年8月30日
  • Photoprism教程:建立你的私人云相册 开源速递

    Photoprism教程:建立你的私人云相册

    0 0222
    小半的头像 小半
    2024年3月21日
  • Spring Boot: 怎么封装一个易用的 RESTful 工具库 技术漫谈

    Spring Boot: 怎么封装一个易用的 RESTful 工具库

    0 0100
    小半的头像 小半
    2024年3月19日
  • MySQL特殊恢复实战-第二讲:各种文件丢失,如何救库 数据库

    MySQL特殊恢复实战-第二讲:各种文件丢失,如何救库

    0 0128
    小半的头像 小半
    2024年3月17日
  • 前端从零开始(37)HTML5 多线程 前端开发

    前端从零开始(37)HTML5 多线程

    0 094
    小半的头像 小半
    2022年6月1日
  • SpringCloudGateway网关原理与配置 微信精选

    SpringCloudGateway网关原理与配置

    0 0244
    小半的头像 小半
    2023年4月23日
  • wait和notify 微信精选

    wait和notify

    0 0184
    小半的头像 小半
    2023年1月11日
  • Ribbon从入门到源码解析​ 后端开发

    Ribbon从入门到源码解析​

    0 0143
    小半的头像 小半
    2022年5月19日
  • Elasticsearch Data Stream 数据流使用 技术漫谈

    Elasticsearch Data Stream 数据流使用

    0 0109
    小半的头像 小半
    2024年3月8日
  • MySQL架构与历史 数据库

    MySQL架构与历史

    0 0115
    小半的头像 小半
    2022年9月20日

发表回复

登录后才能评论

站长精选

  • 是时候丢掉 BeanUtils 了!

    是时候丢掉 BeanUtils 了!

    2023年8月24日

  • 腾讯开源了一款 Markdown 编辑器,易扩展、功能全,很好用!

    腾讯开源了一款 Markdown 编辑器,易扩展、功能全,很好用!

    2023年8月3日

  • 如何设计一套可以应对千万级数据量、分布式和高可用的对账系统?

    如何设计一套可以应对千万级数据量、分布式和高可用的对账系统?

    2023年11月5日

  • 线程池执行的用户任务抛出异常会怎样?

    线程池执行的用户任务抛出异常会怎样?

    2022年11月15日

  • 轻松解决定制化需求,这款 Java 插件化热插拔框架开源了!

    轻松解决定制化需求,这款 Java 插件化热插拔框架开源了!

    2024年3月6日

  • 还在用 SimpleDateFormat 做时间格式化?小心项目崩掉!

    还在用 SimpleDateFormat 做时间格式化?小心项目崩掉!

    2023年2月14日

  • 巧用『责任链模式』来优化 参数多重校验,非常优雅!

    巧用『责任链模式』来优化 参数多重校验,非常优雅!

    2022年12月19日

  • 支付宝:服务端如何防止订单重复支付?

    支付宝:服务端如何防止订单重复支付?

    2023年4月19日

  • git提交错了?别慌,直接删除提交记录

    git提交错了?别慌,直接删除提交记录

    2024年5月1日

  • 还在用 if 做条件验证?来试试用 @Validated,这样写更优雅!

    还在用 if 做条件验证?来试试用 @Validated,这样写更优雅!

    2023年8月10日

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!

深圳SEO优化公司荷坳网站设计模板民治网站优化推广塘坑建设网站坪地网页制作丹竹头网站建设龙岗营销网站南山设计网站南澳关键词按天计费民治关键词排名包年推广观澜品牌网站设计罗湖设计公司网站坪地百度seo龙岗网站排名优化南山网站推广石岩网页制作龙华建站布吉网站建设设计塘坑seo网站推广罗湖百度竞价大芬网站设计坪地网站搜索优化福田网站建设设计南山百度关键词包年推广龙华关键词按天收费龙岗英文网站建设南联企业网站设计双龙模板推广深圳关键词按天扣费广州网站制作丹竹头网络广告推广歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化