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,一个ObjectFactorylambda表达式,表达式中有个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-methodreplace-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类放入到BeanDefinitionMethodOverrides 属性中,在进行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)的更多相关文章

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

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

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

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

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

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

  4. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  5. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

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

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

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

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

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

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

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

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

  10. 初探Spring源码之Spring Bean的生命周期

    写在前面的话: 学无止境,写博客纯粹是一种乐趣而已,把自己理解的东西分享出去,不意味全是对的,欢迎指正! Spring 容器初始化过程做了什么? AnnotationConfigApplication ...

随机推荐

  1. 使用 Spring 有哪些方式?

    使用 Spring 有以下方式: 作为一个成熟的 Spring Web 应用程序. 作为第三方 Web 框架,使用 Spring Frameworks 中间层. 用于远程使用. 作为企业级 Java ...

  2. 学习openldap01

    Linux 下openldap的详细介绍,搭建,配置管理,备份,案例 Ldap  服务应用指南 兼容(5.X&6.X) 1.1  Ldap 目录服务介绍 1.1.1 什么是目录服务(activ ...

  3. Idea学习之"重启或清理IEDA缓存"

    idea的重启 如下图所示:第1步:通过File–>Invalidate Caches进入重启窗口: 第2步:选择自己所需要的重启方式,四个按钮,一共三种重启方式: 四个按钮的说明 Invali ...

  4. ctfhub rce 命令注入 过滤cat 过滤空格 过滤目录分隔符

    命令注入 源码直接给了出来尝试127.0.0.1;ls 发现一串数字的php文件cat查看 127.0.0.1|cat 233382768628619.php 查看源码发现flag 过滤cat 这题和 ...

  5. 4.3 ROS工作空间覆盖

    4.3 ROS工作空间覆盖 所谓工作空间覆盖,是指不同工作空间中,存在重名的功能包的情形. ROS 开发中,会自定义工作空间且自定义工作空间可以同时存在多个,可能会出现一种情况: 虽然特定工作空间内的 ...

  6. 模型预测控制(MPC)简介

    1.引言 在当今过程控制中,PID当然是用的最多的控制方法,但MPC也超过了10%的占有率.MPC是一个总称,有着各种各样的算法.其动态矩阵控制(DMC)是代表作.DMC采用的是系统的阶跃响应曲线,其 ...

  7. 使用el-tree-transfer的方式

    1.首先在组件中引入el-tree-transfer 2.然后在template中使用注册后的组件 参数:title 说明:标题 类型:Array 必填:false 默认:["源列表&quo ...

  8. Spark入门之idea编写Scala脚本

    一.安装Scala插件 1.File->Settings 2.Plugins->Msrketplace->搜索Scala并安装 (或者自己下载合适的scala版本,教程:自己给ide ...

  9. PAT B1002写出这个数

    读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式: 每个测试输入包含 1 个测试用例,即给出自然数 n 的值.这里保证 n 小于 1. 输出格式: 在一行内输出 n 的 ...

  10. Conda 配置 Python 环境

    目录 前言 一.Conda 是什么 二.如何获取 三.使用 Conda 命令配置多环境 1.创建新环境 2.激活新环境 3.配置新环境 4.退出新环境 5.检查所有环境 6.检查所有安装的包 7.删除 ...