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

  • 创建bean的实例
  • 给实例化出来的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的时机则是下面这行代码运行时触发的。

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

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

1.静态工厂方法
public class FactoryInstance {

    public FactoryInstance() {
System.out.println("instance by FactoryInstance");
}
}
public class MyBeanFactory {

    public static FactoryInstance getInstanceStatic(){
return new FactoryInstance();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> <bean id="factoryInstance" class="spring.service.instance.MyBeanFactory"
factory-method="getInstanceStatic"/>
</beans>

输出结果为:

instance by FactoryInstance

2.实例工厂方法
public class MyBeanFactory {

    /**
* 实例工厂创建bean实例
*
* @return
*/
public FactoryInstance getInstance() {
return new FactoryInstance();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- 工厂实例 -- >
<bean id="myBeanFactory" class="MyBeanFactory"/>
<bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>
</beans>

输出结果为:

instance by FactoryInstance

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

1.无参构造函数实例化(默认的)
public class ConstructorInstance {

    public ConstructorInstance() {
System.out.println("ConstructorInstance none args");
} }
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/>
</beans>

输出结果为:

ConstructorInstance none args

1.有参构造函数实例化
public class ConstructorInstance {

    private String name;

    public ConstructorInstance(String name) {
System.out.println("ConstructorInstance with args");
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} }
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance">
<constructor-arg index="0" name="name" value="test constructor with args"/>
</bean>
</beans>

输出结果为:

ConstructorInstance with args

源码阅读

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

doCreateBean方法具体实现在AbstractAutowireCapableBeanFactory类,感兴趣的朋友可以进去看看调用链。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
//第一步 创建bean实例 还未进行属性填充和各种特性的初始化
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); Object exposedObject = bean;
try {
// 第二步 进行属性填充
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 第三步 初始化bean 执行初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}catch (Throwable ex) {
// 抛相应的异常
} // Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}

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

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

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

  • instantiateUsingFactoryMethod 工厂方法实例化的具体实现
  • autowireConstructor 有参构造函数实例化的具体实现
  • instantiateBean 默认实例化具体实现(无参构造函数)

实例化策略(cglib or 反射)

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

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

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

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

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

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
// 在这里选择一种策略进行实例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}

选择使用反射还是cglib

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

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

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

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

由于篇幅省略了部分代码

【转载】Spring 源码分析之 bean 实例化原理的更多相关文章

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

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

  2. Spring 源码分析之 bean 依赖注入原理(注入属性)

         最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...

  3. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  4. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

  5. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  6. Spring源码-IOC部分-Bean实例化过程【5】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  7. 【Spring源码分析】Bean加载流程概览(转)

    转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...

  8. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  9. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

随机推荐

  1. Python Numpy 矩阵级基本操作(2)

    1.开方与求e指数 import numpy as np from numpy.matlib import randn print "Test sqrt and exp" arr ...

  2. 转 mysql查询结果输出到文件

    mysql查询结果输出到文件   mysql查询结果导出/输出/写入到文件 方法一:直接执行命令:mysql> select count(1) from table  into outfile ...

  3. 利用os和pandas来合并当前目录下所有excel文件

    #1.引入模块 import os import pandas as pd #2.取出指定目录下的全部excel文件路径 path="C:\\TEST" dirlist=[] fo ...

  4. 常用Javascript方法

    一,检测是否是Array 1,通过constructor检测 function isArray(value){ return value && typeof value === 'ob ...

  5. 粗糙的区别prepareStatement:(为Statement的子类)与Statement

    区别: prepareStatement:(为Statement的子类) conn = DBFactory.getInstance().getImpl().getConnection(); //方式一 ...

  6. 关于solr的一些知识

    简单了解 怎么理解Solr是个什么东西呢? 引用官网的介绍, Solr is the popular, blazing-fast, open source enterprise search plat ...

  7. sql语句练习50题(Mysql版-详加注释)

    表名和字段 1.学生表       Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别 2.课程表       Course(c_id, ...

  8. Fastjson <= 1.2.47 远程命令执行漏洞

    一.漏洞利用过程 查看java版本:java -version jdk版本大1.8 openjdk versin "1.8.0_222" 下载漏洞利用文件:git clone ht ...

  9. 在阅读众多的blog中,我学到了什么

    写博客的人,自然会读别人的博客:读博客的人,不一定会写博客.但是这两种人之间的差别是很大的 在最近在一段时间,发现了一个好的博客,通过该博客的友链,发现了新大陆.... 从Jeff Wong开始,到老 ...

  10. HIVE了解及SQL基础命令

    hive(数据仓库工具) Hive是一个数据仓库基础工具在Hadoop中用来处理结构化数据.它架构在Hadoop之上,总归为大数据,并使得查询和分析方便.并提供简单的sql查询功能,可以将sql语句转 ...