Spring 源码(11)Spring Bean 的创建过程(2)
Spring Bean
的创建过程介绍了FactoryBean
的创建方式,那么接下来介绍不是FactoryBean
的创建方式,在创建过程中,又会分为单例的Bean的创建,原型类型的Bean的创建等。一般来说在Spring中几乎所有对象都是单例创建的,除非有其他业务需要设置为其他作用域的Bean,所以重点以创建单例Bean为例。
单例Bean的创建
在创建时会调用getBean
,然后doGetBean
,一般来说在Spring
中只要是do
开头方法基本就是真正干活的方法,所以我们看doGetBean
方法的源码:
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 解析成规范的Bean name ,因为可能是FactoryBean加了& 前缀的Bean或者是有别名的Bean
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 获取缓存中的Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 如果缓存中没有,那么就会按照单例或者多例的方式创建
else {
// 省略代码....
// Check if bean definition exists in this factory.
// 检查父类容器
// 省略代码....
if (!typeCheckOnly) {
// 标记已经被创建
markBeanAsCreated(beanName);
}
try {
// 合并BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 判断是否存在依赖的Bean的创建,比如dependsOn 依赖 A 这个Bean,那么就需要先创建A这个bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// 省略代码....
}
// 注册依赖的Bean,放在集合中
registerDependentBean(dep, beanName);
try {
// 创建Bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
// 省略代码....
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
// 如果是单例的,就去创建Bean
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建Bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 省略代码....
}
});
// 获取bean对象,会进行检查获取对象是否是FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型作用的创建方式
else if (mbd.isPrototype()) {
// 省略代码....
}
else {
// 省略代码....
}
}
catch (BeansException ex) {
// 省略代码....
}
}
// 省略代码....
return (T) bean;
}
去掉不重要的代码,可以看到首先是从缓存中获取,如果没有获取到就进行一些列检查,最终检查是否单例的Bean
,如果是,那么就会调用getSingleton
方法,传入一个beanName
,一个ObjectFactory
的lambda
表达式,表达式中有个createBean
方法,这个方法就是创建的Bean
方法。
那什么时候调用crateBean
方法呢?
答案是执行lambda
表达式的具体方法时执行,我们先看看这个ObjectFactory
接口是啥?
ObjectFactory 对象工厂
直接看源码:
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
这个接口是一个函数式接口,可以用于lambda
表达式直接使用,在调用getObject
方法时就是真正执行lambda
表达式中的方法。
具体看看getSingleton
方法的源码:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
// 省略代码....
}
// 省略代码....
// 检查 并添加正在创建的单例对象到集合中
beforeSingletonCreation(beanName);
// 设置为新的单例对象标识
boolean newSingleton = false;
// 设置异常集合,出现异常时将异常加入到集合中
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 执行具体的方法,调用crateBean方法
singletonObject = singletonFactory.getObject();
// 标识为新的单例对象
newSingleton = true;
}
catch (IllegalStateException ex) {
// 省略代码....
}
catch (BeanCreationException ex) {
// 省略代码....
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 检查 并移除正在创建的单例对象
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 加入到缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
这里执行完之后就会执行到lambda
表达式中的createBean
方法:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 省略代码....
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// 解析Bean的Class 用于反射创建对象
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// 方法覆盖准备 lookup-method replace-method
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
// 省略代码....
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 解析提前实例化,使用InstantiationAwareBeanPostProcessor实现
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
// 省略代码....
}
try {
// 实例化 + 初始化 Bean
// 真正的创建Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 省略代码....
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// 省略代码....
}
catch (Throwable ex) {
// 省略代码....
}
}
首先是进行了Bean
的类型的解析,主要是用于后面的反射创建对象时使用,并设置到RootBeanDefinition
中,然后进行方法覆盖操作。
Spring方法覆盖实战
方法覆盖就是使用了lookup-method
和replace-method
标签的时候,就会进行方法的覆盖。方法覆盖有什么用处呢?
一般来说方法覆盖就是解决单例对象引用多例对象的时候使用方法覆盖。
做个实验试试:
定义一个房子类,用于停车
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public abstract class MyHouse {
public abstract MyCar park();
}
定义我的车
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public interface MyCar {
/**
* 买一辆车
* @return 车
*/
MyCar buy();
}
定义实现类:
宝马车:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class BMW implements MyCar{
@Override
public MyCar buy() {
return this;
}
}
奔驰车:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class Ben implements MyCar{
@Override
public MyCar buy() {
return this;
}
}
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="myHouse1" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
<lookup-method name="park" bean="bmw"/>
</bean>
<bean id="myHouse2" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
<lookup-method name="park" bean="ben"/>
</bean>
<!-- 设置为原型-->
<bean id="bmw" class="com.redwinter.test.methodoverride.lookupmethod.BMW" scope="prototype"/>
<bean id="ben" class="com.redwinter.test.methodoverride.lookupmethod.Ben"/>
</beans>
测试类:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class LookupTest {
/**
* lookup-method 用来解决单例对象多例对象的
*/
@Test
public void lookupTest(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:method-override.xml");
MyHouse myHouse = (MyHouse) context.getBean("myHouse1");
MyHouse myHouse1 = (MyHouse) context.getBean("myHouse1");
System.out.println(myHouse.park());
System.out.println(myHouse1.park());
}
}
输出:
com.redwinter.test.methodoverride.lookupmethod.BMW@4a765
com.redwinter.test.methodoverride.lookupmethod.BMW@3e6358
这里Myhouse
是一个单例的对象,myHouse1
调用的方法每次调用都是不同的对象。
Spring是如何实现方法覆盖的?
源码过于繁琐和复杂,这里直接看执行流程:
Spring
在加载BeanDefinition
的时候,执行 parseLookupOverrideSubElements
这个方法的时候只要设置了lookup-method
标签就会创建一个LookupOverride
类放入到BeanDefinition
的 MethodOverrides
属性中,在进行Bean
的创建的时候,就会判断这个属性值是否有值,如果有那么就会在对象实例化时获取一个实例化策略,然后执行实例化,就、就会调用SimpleInstantiationStrategy#instantiate
方法,然后使用CGLIB
进行实例化,创建出一个Enhancer
增强类,并且设置一个回调类型为:
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
最终在执行方法的时候就会调用到回调类LookupOverrideMethodInterceptor
拦截器上,然后执行Bean
的创建:
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(lo != null, "LookupOverride not found");
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
if (StringUtils.hasText(lo.getBeanName())) {
// 创建Bean
Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
this.owner.getBean(lo.getBeanName()));
// Detect package-protected NullBean instance through equals(null) check
return (bean.equals(null) ? null : bean);
}
else {
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));
}
}
这篇文章就介绍到这里,下一篇继续。
Spring 源码(11)Spring Bean 的创建过程(2)的更多相关文章
- 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 bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- 【Spring源码分析】Bean加载流程概览(转)
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- 初探Spring源码之Spring Bean的生命周期
写在前面的话: 学无止境,写博客纯粹是一种乐趣而已,把自己理解的东西分享出去,不意味全是对的,欢迎指正! Spring 容器初始化过程做了什么? AnnotationConfigApplication ...
随机推荐
- python udp socket通信
前段时间学习了一下c++的socket通信,但发现那玩意儿比较复杂还是转向python了,下面就是一个简单的udpsocket通信程序,欢迎大佬前来指正 udp聊天 import socket # 创 ...
- HTML5 & CSS3 内容收集(1)
1. HTML发展历史介绍 2. 浏览器支持 2.1 新增标签支持 在html5 中新增了很多的标签,其中包括8个新增语义结构标签.header, section, footer, aside, na ...
- Codepen 每日精选(2018-4-4)
按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以打开原始页面. 纯 css 画的扫雷游戏界面https://codepen.io/alejuss/fu... 线条简单的小 ...
- Redis 中的过期删除策略和内存淘汰机制
Redis 中 key 的过期删除策略 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 ...
- python-输入输出-计算字符串中的数
将字符串中的每个数都抽取出来,然后统计所有数的个数并求和. 输入格式: 一行字符串,字符串中的数之间用1个空格或者多个空格分隔. 输出格式: 第1行:输出数的个数.第2行:求和的结果,保留3位小数. ...
- java反射相关
反射的机制:反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言 ...
- vs技巧 - 调试asp.net core源码
学习asp.net core的方式除了看官方文档,看源码是也是一种很好的方式.本文介绍一种方法,简单配置vs,无需第三方插件就可以将asp.net core的源码链接自己的项目,随时穿梭于core的源 ...
- C语言-操作符与表达式
C语言入门之操作符与表达式 前言 本篇文章主要包括各种操作符的介绍与表达式求值,欢迎各位小伙伴与我一起学习. 一.操作符 分类 算术操作符 移位操作符 位操作符 赋值操作符 单目运算符 关系操作符 逻 ...
- 32位x86处理器架构
我们看看32 位 x86 处理器的基本架构特点.这些处理器包括了 Intel IA-32 系列中的成员和所有 32 位 AMD 处理器. 操作模式 x86 处理器有三个主要的操作模式:保护模式.实地址 ...
- 安全市场迎来新挑战,FinClip助力车联网数据安全
随着汽车工业的发展与电子技术的进步,智能汽车迎来了前所未有的蓬勃发展,随着汽车电动化.网联化.智能化交融发展,车辆运行安全.数据安全和网络安全风险交织叠加,安全形势更加复杂严峻......