博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 源码分析之 bean 实例化原理
阅读量:6849 次
发布时间:2019-06-26

本文共 6229 字,大约阅读时间需要 20 分钟。

本次主要想写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();    }}复制代码
复制代码

输出结果为:

instance by FactoryInstance

2.实例工厂方法
public class MyBeanFactory {    /**     * 实例工厂创建bean实例     *     * @return     */    public FactoryInstance getInstance() {        return new FactoryInstance();    }}复制代码

输出结果为:

instance by FactoryInstance

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

1.无参构造函数实例化(默认的)
public class ConstructorInstance {    public ConstructorInstance() {        System.out.println("ConstructorInstance none args");    }}复制代码
复制代码

输出结果为:

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;    }}复制代码
复制代码

输出结果为:

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() {					@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 源码深度剖析》这本书

复制代码

如果使用了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);		}	}复制代码

由于篇幅省略了部分代码

其他文章

转载于:https://juejin.im/post/5ca42bfa6fb9a05e17799e07

你可能感兴趣的文章
使用sql server的存储过程来讲中文汉字转化为拼音字符的代码实例教程
查看>>
kvm虚拟化学习笔记(二十)之convirt安装linux系统
查看>>
MoNyog 5.72 mysql监控工具的介绍
查看>>
nagios 安装
查看>>
在这里分享技术开发的心得
查看>>
音频工作(采集)模式是什么?
查看>>
教你如何解决android logcat不打印信息的问题
查看>>
Shell中的IFS解惑
查看>>
vim显示行号、语法高亮、自动缩进的设置
查看>>
创建后台进程
查看>>
Silverlight三维透视+倒影效果
查看>>
leetcode -- String to Integer (atoi)
查看>>
Packet Tracer 5.3 环境试验
查看>>
项目范围管理论文标题
查看>>
SQL Server 存储过程
查看>>
Centos搭建Samba
查看>>
618商战大片谢幕,销量冠军竟然有两个?
查看>>
移动开发编程资源大全,值得一试!
查看>>
看图了解RocksDB
查看>>
我的友情链接
查看>>