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. SVN之屏蔽不需要提交的xml等文件

    SVN之屏蔽不需要提交的xml等文件 在默认"Default changelist"中是我们正常需要提交的文件 在"不需要提交的文件"中存储的是一些线下环境需要 ...

  2. InfoQ Trends Report

    InfoQ Trends Report InfoQ Trends Report Culture & Methods Trends Report - March 2021 DevOps and ...

  3. 用纯CSS美化radio和checkbox

    Radio和checkbox需要美化吗?答案是必须的,因为设计风格一直都会变化,原生的样式百年不变肯定满足不了需求. 先看看纯CSS美化过后的radio和checkbox效果:查看. 项目地址:mag ...

  4. 微信小程序&mpvue问题总结(1)

    微信小程序进入到首页的时候,日志打印出"created", "onlaunch", "mounted",具体代码如下:那么,在小程序中 cr ...

  5. ES6-11学习笔记--Promise

    Promise是ES6异步编程解决方案之一,简化以前ajax的嵌套地狱,增加代码可读性.   基本用法: resolve,成功 reject,失败 let p = new Promise((resol ...

  6. 在node中通过cors跨域。

    cors : 全称 cross origin resource share  跨资源共享 在nodejs 中可以通过在服务器端设置代码如下实现cors跨域 res.setHeader('Access- ...

  7. 支付宝小程序中“<”号写法

    今天遇到一个小问题,记录一下 "<"号在h5页面都是可以直接显示的,但是在运行支付宝小程序时报错,找了一个解决办法 <text> {{char_lt}} 18.5 ...

  8. Visual Studio 2022 git error Unable to negotiate with xx.xxx.xxxx port 22: no matching host key type found. Their offer: ssh-rsa

    前言 前两天因为升级了Git导致git提交拉取的时候都提示下面这个异常,然后经过一番折腾以后终于把这个问题解决了.但是今天我升级了下Visual Studio 2022将其升级到了17.1.3版本然后 ...

  9. c语言实现循环单链表

    //初始化 Node*InitList() { Node*head=(Node*)malloc(sizeof(Node)); head->next=NULL; head->data=-1; ...

  10. 用来创建用户docker registry认证的Secret

    集群环境:1.k8s用的是二进制方式安装2.操作系统是linux (centos)3.操作系统版本为 7.4/7.94.k8s的应用管理.node管理.pod管理等用rancher.k8s令牌以及ma ...