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 ...
随机推荐
- 『忘了再学』Shell基础 — 7、Bash基本功能(多命令顺序执行)
目录 1.多命令执行符: 2.多命令执行符&& 3.多命令执行符|| 4.&&和||联合应用 Linux系统支持多条命令顺序执行,就是我可以依次输入多条命令后,统一按E ...
- servlet中的HttpServletResponse对象
当有多个客户端浏览器去请求Tomcat时,Tomcat会为每一个客户端浏览器创建一对独立的HttpServletRequest与HttpServletResponse对象 HttpServletRes ...
- (5) 图和表(Figure and Table) 【论文写作】
- 你不需要基于 CSS Grid 的栅格布局系统
在过去的几个星期里,我开始看到基于 CSS Grid 的布局框架和栅格系统的出现.我们惊讶它为什么出现的这么晚.但除了使用 CSS Grid 栅格化布局,我至今还没有看到任何框架能提供其他有价值的东西 ...
- ES6-11学习笔记--Symbol
Symbol:一种新的原始数据类型 声明方式: let s1 = Symbol() let s2 = Symbol() console.log(s1); // Symbol() console.l ...
- 单例模式的实现Singleton和MonoSingleton
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> / ...
- C#内置委托类型Func和Action对比及用法
C#的内置委托类型 Func Action 返回值 有(只有一个Tresult) 无 重载 17个(0参-16参) 17个(0参-16参) 泛型 支持 支持 系统内置 是 是 是否需要声明 否 否 c ...
- java中的访问控制有什么用?如何用法?请举例
9.访问控制 [新手可忽略不影响继续学习] 访问控制有什么用?在软件公司里是这么用的,我们想像一种场景,在你的类中,你编了三个私有方法,马克-to-win,别人当然都用不了,但在类外,你也是用不了的, ...
- Android限制输入框内容
<EditText android:id="@+id/temper" android:hint="36.2" android:digits="1 ...
- VMware workstation16 许可证
ZF3R0-FHED2-M80TY-8QYGC-NPKYF YF390-0HF8P-M81RQ-2DXQE-M2UT6 ZF71R-DMX85-08DQY-8YMNC-PPHV8 若资金允许,请购买正 ...