Spring 源码分析之 bean 实例化原理
本次主要想写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的时机则是下面这行代码运行时触发的。更多关于懒加载的内容可以参考这篇文章。
Spring lazy-init 原理分析
  @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方法具体实现在doCreateBeanAbstractAutowireCapableBeanFactory类,感兴趣的朋友可以进去看看调用链。
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-method和replaced-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 实例化原理的更多相关文章
- 【转载】Spring 源码分析之 bean 实例化原理
		
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
 - Spring 源码分析之 bean 依赖注入原理(注入属性)
		
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
 - Spring Ioc源码分析系列--Bean实例化过程(二)
		
Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...
 - Spring Ioc源码分析系列--Bean实例化过程(一)
		
Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...
 - Spring源码分析之Bean的创建过程详解
		
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
 - Spring源码-IOC部分-Bean实例化过程【5】
		
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
 - 【Spring源码分析】Bean加载流程概览
		
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
 - 【Spring源码分析】Bean加载流程概览(转)
		
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
 - Spring源码分析:Bean加载流程概览及配置文件读取
		
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
 
随机推荐
- mongodb异常恢复
			
构造mongdb异常 启动mongodb,bash mongodb.sh #!/bin/bash pid_file=/var/run/mongodb/mongod.pid pid_dir=/var/r ...
 - cdnbest获取,删除,增加,修改域名列表,高级设置api示例
			
<?php $uid = 28; $vhost = 'asdfw'; $token = getToken($uid, $vhost); print_r($token); //获取token fu ...
 - 《CSAPP》地址翻译
			
本节所使用的符号: 地址翻译 地址翻译是一个N元素的虚拟地址空间(VAS)中的元素和一个M元素的物理地址空间(PAS)中元素之间的映射. 映射实现: MMU利用页表来实现这种映射.CPU中的一个控制寄 ...
 - 网络请求Adapter添加数据
			
一般在开发中我们都需要在listview中添加数据显示在界面上 1.首先我们会在布局中写一个listview <FrameLayout xmlns:android="http://sc ...
 - js控制easyui文本框例子及控制html例子
			
easyui $('#value').textbox('setValue',''); //赋值 $('#value').textbox({required:false});//必填,方框变红 $('# ...
 - Java14-java语法基础(十三)接口
			
Java14-java语法基础(十三)接口 一.接口 1.接口的作用 Java出于安全性.简化程序结构的考虑,不支持多继承而仅支持单继承.然而实际问题中很多情况下仅仅依靠单继承并不能将复杂的问题描述清 ...
 - centos 7 一些命令
			
su 切换到管理员账户cd 'wo shi mu lu'ls 查看 当前目录或者文件tar -xvzf pip-10.0.1.tar.gz 解压 文件systemctl restart network ...
 - CSS实现左侧多级菜单栏
			
首先看要实现的效果, 主要是关心技术实现, 所以没怎么美化 我也是初学html, 所以写的比较啰嗦 1. 使用列表将内容显示出来 <!DOCTYPE html><html>&l ...
 - java多线程系列17 ThreadLocal
			
ThreadLocal 叫做 线程局部变量 在详细解释之前 先看一段代码 public class ThreadLocalTest { private static ThreadLocal<In ...
 - Spring 的属性注入
			
一.注入方式 (1)set方法注入 (2)构造函数注入 (3)p名称空间注入 (4)spel注入 二.复杂类型注入