Spring 源码分析之 bean 实例化原理

本次主要想写spring bean的实例化相关的内容。创建spring bean 实例是spring bean 生命周期的第一阶段。bean 的生命周期主要有如下几个步骤:

  • <font color='red'>创建bean的实例</font>
  • 给实例化出来的bean填充属性
  • 初始化bea
  • 通过IOC容器使用bean
  • 容器关闭时销毁bean

在实例化bean之前在BeanDefinition里头已经有了所有需要实例化时用到的元数据,接下来spring 只需要选择合适的实例化方法以及策略即可。实例化方法有两大类分别是工厂方法和构造方法实例化,后者是最常见的。spring默认的实例化方法就是无参构造函数实例化。
如我们在xml里定义的 <bean id="xxx" class="yyy"/> 以及用注解标识的bean都是通过默认实例化方法实例化的。

  • 两种实例化方法(构造函数 和 工厂方法)
  • 源码阅读
  • 实例化策略(cglib or 反射)

两种实例化方

使用适当的实例化方法为指定的bean创建新实例:工厂方法,构造函数实例化。

代码演示

启动容器时会实例化所有注册的bean(lazy-init懒加载的bean除外),对于所有单例非懒加载的bean来说当从容器里获取bean(getBean(String name))的时候不会触发,实例化阶段,而是直接从缓存获取已准备好的bean,而生成bean的时机则是下面这行代码运行时触发的。更多关于懒加载的内容可以参考这篇文章。
Spring lazy-init 原理分析

1 @Test 2 public void testBeanInstance(){ 3 // 启动容器 4 ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); 5 }

一 使用工厂方法实例化(很少用)

1.静态工厂方法
1public class FactoryInstance { 2 3 public FactoryInstance() { 4 System.out.println("instance by FactoryInstance"); 5 } 6} 7 8 9public class MyBeanFactory { 10 11 public static FactoryInstance getInstanceStatic(){ 12 return new FactoryInstance(); 13 } 14} 15 16 17<?xml version="1.0" encoding="UTF-8"?> 18<beans xmlns="http://www.springframework.org/schema/beans" 19 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 20 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 21 22 <bean id="factoryInstance" class="spring.service.instance.MyBeanFactory" 23 factory-method="getInstanceStatic"/> 24</beans>

输出结果为:

<font color=red >instance by FactoryInstance</font>

2.实例工厂方法
1public class MyBeanFactory { 2 3 /** 4 * 实例工厂创建bean实例 5 * 6 * @return 7 */ 8 public FactoryInstance getInstance() { 9 return new FactoryInstance(); 10 } 11} 12 13 14<?xml version="1.0" encoding="UTF-8"?> 15<beans xmlns="http://www.springframework.org/schema/beans" 16 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 17 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 18 <!-- 工厂实例 -- > 19 <bean id="myBeanFactory" class="MyBeanFactory"/> 20 <bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/> 21</beans>

输出结果为:

<font color=red >instance by FactoryInstance</font>

二 使用构造函数实例化(无参构造函数 & 有参构造函数)

1.无参构造函数实例化(默认的)
1public class ConstructorInstance { 2 3 public ConstructorInstance() { 4 System.out.println("ConstructorInstance none args"); 5 } 6 7} 8 9 10<?xml version="1.0" encoding="UTF-8"?> 11<beans xmlns="http://www.springframework.org/schema/beans" 12 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 13 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 14 <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/> 15</beans>

输出结果为:

<font color=red >ConstructorInstance none args</font>

1.有参构造函数实例化
1public class ConstructorInstance { 2 3 private String name; 4 5 public ConstructorInstance(String name) { 6 System.out.println("ConstructorInstance with args"); 7 this.name = name; 8 } 9 10 public String getName() { 11 return name; 12 } 13 14 public void setName(String name) { 15 this.name = name; 16 } 17 18} 19 20 21<?xml version="1.0" encoding="UTF-8"?> 22<beans xmlns="http://www.springframework.org/schema/beans" 23 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 24 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 25 26 <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"> 27 <constructor-arg index="0" name="name" value="test constructor with args"/> 28 </bean> 29</beans>

输出结果为:

<font color=red >ConstructorInstance with args</font>

源码阅读

下面这段是 有关spring bean生命周期的代码,也是我们本次要讨论的bean 实例化的入口。

<font color="red">doCreateBean</font>方法具体实现在<font color="red">doCreateBeanAbstractAutowireCapableBeanFactory</font>类,感兴趣的朋友可以进去看看调用链。

1protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { 2 //第一步 创建bean实例 还未进行属性填充和各种特性的初始化 3 BeanWrapper instanceWrapper = null; 4 if (instanceWrapper == null) { 5 instanceWrapper = createBeanInstance(beanName, mbd, args); 6 } 7 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); 8 Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); 9 10 Object exposedObject = bean; 11 try { 12 // 第二步 进行属性填充 13 populateBean(beanName, mbd, instanceWrapper); 14 if (exposedObject != null) { 15 // 第三步 初始化bean 执行初始化方法 16 exposedObject = initializeBean(beanName, exposedObject, mbd); 17 } 18 }catch (Throwable ex) { 19 // 抛相应的异常 20 } 21 22 // Register bean as disposable. 23 try { 24 registerDisposableBeanIfNecessary(beanName, bean, mbd); 25 }catch (BeanDefinitionValidationException ex) { 26 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 27 } 28 return exposedObject; 29 } 30

我们这里只需关注第一步创建bean实例的流程即可
instanceWrapper = createBeanInstance(beanName, mbd, args);

1protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { 2 // Make sure bean class is actually resolved at this point. 3 Class<?> beanClass = resolveBeanClass(mbd, beanName); 4 // 使用工厂方法进行实例化 5 if (mbd.getFactoryMethodName() != null) { 6 return instantiateUsingFactoryMethod(beanName, mbd, args); 7 } 8 // Need to determine the constructor... 9 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); 10 // 使用带参构造函数初始化 11 if (ctors != null || 12 mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || 13 mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { 14 15 return autowireConstructor(beanName, mbd, ctors, args); 16 } 17 18 // 默认实例化方式 无参构造实例化 19 return instantiateBean(beanName, mbd); 20 }

上面代码就是spring 实现bean实例创建的核心代码。这一步主要根据BeanDefinition里的元数据定义决定使用哪种实例化方法,主要有下面三种:

  • <font color="red">instantiateUsingFactoryMethod</font> 工厂方法实例化的具体实现
  • <font color="red">autowireConstructor</font> 有参构造函数实例化的具体实现
  • <font color="red">instantiateBean</font> 默认实例化具体实现(无参构造函数)

实例化策略(cglib or 反射)

<font color="red">工厂方法的实例化手段没有选择策略直接用了发射实现的</font>
<font color="red">实例化策略都是对于构造函数实例化而言的</font>

上面说到的两构造函数实例化方法不管是哪一种都会选一个实例化策略进行,到底选哪一种策略也是根据BeanDefinition里的定义决定的。

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);

上面这一行代码就是选择实例化策略的代码,进入到上面两种方法的实现之后发现都有这段代码。

下面选一个instantiateBean的实现来介绍

1protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { 2 try { 3 Object beanInstance; 4 final BeanFactory parent = this; 5 if (System.getSecurityManager() != null) { 6 beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() { 7 @Override 8 public Object run() { 9 return getInstantiationStrategy().instantiate(mbd, beanName, parent); 10 } 11 }, getAccessControlContext()); 12 } 13 else { 14 // 在这里选择一种策略进行实例化 15 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); 16 } 17 BeanWrapper bw = new BeanWrapperImpl(beanInstance); 18 initBeanWrapper(bw); 19 return bw; 20 } 21 catch (Throwable ex) { 22 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); 23 } 24 }

选择使用反射还是cglib

先判断如果beanDefinition.getMethodOverrides()为空也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷,但是如果使用了这两个特性,在直接使用反射的方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例。---引用自《spring 源码深度剖析》这本书

1 <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance" > 2 <lookup-method name="getName" bean="xxx"/> 3 <replaced-method name="getName" replacer="yyy"/> 4 </bean>

如果使用了lookup或者replaced的配置的话会使用cglib,否则直接使用反射。
具体lookup-methodreplaced-method的用法可以查阅相关资料。

1 public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) { 2 // Don't override the class with CGLIB if no overrides. 3 if (bd.getMethodOverrides().isEmpty()) { 4 constructorToUse = clazz.getDeclaredConstructor((Class[]) null); 5 return BeanUtils.instantiateClass(constructorToUse); 6 }else { 7 // Must generate CGLIB subclass. 8 return instantiateWithMethodInjection(bd, beanName, owner); 9 } 10 }

<font color="red"> 由于篇幅省略了部分代码

点赞
收藏

评论区

加载中...

相关推荐

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:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )