Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >简话bean加载

简话bean加载

作者头像
LiosWong
发布于 2018-10-29 09:46:34
发布于 2018-10-29 09:46:34
40100
代码可运行
举报
文章被收录于专栏:后端沉思录后端沉思录
运行总次数:0
代码可运行

首先看示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--no-lazy-init   scope=singleton-->
<bean class="com.lios.service.test.LiosTestA" id="liosTestA"/>
<bean class="com.lios.service.test.LiosTestB" id="liosTestB"/>
<bean class="com.lios.service.test.LiosServiceServiceImpl" id="liosServiceService"/>

ClassPathXmlApplicationContext resource = new ClassPathXmlApplicationContext("app.xml");
BeanFactory beanFactory = resource.getBeanFactory();
LiosServiceServiceImpl liosServiceService = (LiosServiceServiceImpl) beanFactory.getBean("liosServiceService");
liosServiceService.t();

LiosServiceServiceImpl:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class LiosServiceServiceImpl {
    private int i = 1;
    @Autowired
    LiosTestA liosTestA;
    public void t(){
        try {
            liosTestA.testA();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===========>");
    }
}

LiosTestA:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class LiosTestA {
    @Autowired
    LiosTestB liosTestB;
    public void testA(){
        liosTestB.testB();
    }
}

以上代码就是LiosServiceServiceImpl类中引用了LiosTestA,LiosTestA类中引用了LiosTestB,今天的问题是LiosServiceServiceImpl如何引用LiosTestA,LiosTestA如何引用LiosTestB? 看过源码的同学肯定知道, org.springframework.context.support.AbstractApplicationContext#refresh是spring解析xml、初始化bean的入口,该方法里会调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

这个方法会实例化所有的non-lazy-init单例的bean,毫无疑问,这个方法是分析问题的入口,紧跟进去:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

再跟进去,调用了 org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法,再跟进去 getBean(beanName);方法,调用了 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

进入doGetBean方法中,绕了那么多,这个方法才是会真正干事:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//获取beanName
final String beanName = transformedBeanName(name);
//从缓存里获取bean,第一次时毫无疑问,sharedInstance为null
Object sharedInstance = getSingleton(beanName);
//获取RootBeanDefinition,其实用BeanDefinition初始化
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
创建bean
createBean(beanName, mbd, args)

下面分析 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.Class<T>)方法,明显这是把具体实现委托给子类实现了,继续跟: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]),该方法里有一段这样的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
instanceWrapper = createBeanInstance(beanName, mbd, args);

继续跟:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
//.... 后面省略

继续跟 resolveBeanClass()方法,其实该方法里利用反射创建了bean实例,createBeanInstance()方法主要是返回BeanWrapper对象,该对象用bean实例初始化,getWrappedInstance()即可返回bean对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Return the bean instance wrapped by this object, if any.
 * @return the bean instance, or {@code null} if none set
 */
Object getWrappedInstance();

所以上面的LiosServiceServiceImpl、LiosTestA、LiosTestB都是通过resolveBeanClass()方法创建实例,但是里面的引用的属性如何创建呢,那回到 doCreateBean()方法,继续看下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Initialize the bean instance.
Object exposedObject = bean;
try {
    //初始化bean属性的值
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
}

首先分析 populateBean(beanName,mbd,instanceWrapper),为了直接点,直接看后置处理器BeanPostProcessor:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvs == null) {
        return;
    }
  }
}

再看InstantiationAwareBeanPostProcessor的实现类, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);

一直跟进去到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

再跟进去:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);

继续跟进去:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

doResolveDependency方法会根据属性的属性类型去获取引用,继续跟,可以看到 org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法里的这段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
result.put(candidateName, getBean(candidateName));

getBean(candidateName)这个不是调用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)么,没错,是的! 回到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}

这段代码会设置bean中的属性的值,一个真正的bean已经完成了. 再回到 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,看这句代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
initializeBean(beanName, exposedObject, mbd);

跟进后再进入 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

看到InitializingBean、afterPropertiesSet()是否会想起什么呢,如果一个bean实现了InitializingBean接口后,在bean被容器加载时,自动调用afterPropertiesSet()方法,现在明白是咋回事了吧. 说了那么多,总结下LiosServiceServiceImpl类的加载过程,首先容器会加载LiosServiceServiceImpl或者LiosTestA或者LiosTestB,默认是没有明确顺序之分,如果按照先加载LiosTestA的话,会先创建LiosTestA实例,里面的属性LiosTestB值还是为空,然后设置其属性的值,其实就是调用getBean方法,完成LiosTestA的实例创建后,创建LiosServiceServiceImpl套路完全一样,其属性LiosTestA已经在缓存有了,直接获取即可. 最后,bean的加载远远不止这么复杂,文中有错误之处,麻烦指正!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Spring源码】讲讲Bean的生命周期
如果是普通Bean的生命周期,那么上述的回答是真正确的。确实会经历“实例化 -> 属性赋值 -> 初始化 -> 销毁”四个阶段。但是请时刻记住,Spring是个框架,框架的特性除了封装以外,还应当具备扩展性。因此,Spring Bean的生命周期除了上述常见的4个阶段外,还应该具体了解每个阶段的扩展能力,以及Spring提供的一些扩展机制。
有一只柴犬
2024/01/25
2870
【Spring源码】讲讲Bean的生命周期
彻底弄懂Spring中Bean的解析、创建和使用
Spring加载Bean、实例化Bean、获取Bean流程 本文旨在通过分析源码的方式,来剖析Spring加载Bean、实例化Bean、获取Bean的流程,部分核心内容会在源码中说明。内容比较枯燥,慎入! 第一步,启动一个Spring项目 Spring启动入口 从官方文档中我们可以获取到下面这种Spring的启动方式。 我们传入一个test.xml文件 ApplicationContext context = new ClassPathXmlApplicationContext("classp
石奈子
2020/06/28
3.6K0
Spring5.0源码深度解析之Spring是如何利用三级缓存解决循环依赖的问题
Spring已经成为了开发项目的不可缺少的组件了,我们在平常开发项目中难免会遇到以下这些情况,比如说,我有A类和B类,两个业务类都注入到Spring容器里了,且双方都互相注入了,这个时候就会造成循环依赖的问题,相信之前有很多开发者遇到这样的问题吧,不过现在Spring底层已经通过三级缓存来解决了这个循环依赖的问题了。
黎明大大
2021/03/09
1.6K0
【Spring 学习系列】Bean 的生命周期之初始化与销毁
本文将结合一个简单案例,学习 Bean 生命周期中的初始化和销毁阶段的具体内容。
明明如月学长
2022/09/21
3230
【Spring 学习系列】Bean 的生命周期之初始化与销毁
Spring - InstantiationAwareBeanPostProcessor 扩展接口
注意下: Initialization 表示 实例化 (意思是对象还未生成) 。 Instantiation 表示 初始化 (意思是对象已经生成) 。
小小工匠
2022/12/01
5100
万字长文!带你探索 Bean 加载流程
宏观地说,Bean 加载流程大致有三个阶段,分别是实例化 createBeanInstance() 、属性填充 populateBean() 和 初始化 initializeBean(),当 Bean 加载流程执行完毕,Bean 才具备使用条件!对 Bean 加载流程的探索是一段非常煎熬的旅程,你准备好了吗?
程序猿杜小头
2023/03/05
4750
万字长文!带你探索 Bean 加载流程
【Spring源码】- 03 Spring IoC容器启动之Bean创建流程
上篇已经分析完refresh()中大部分方法,也已经把Bean解析成BeanDefinition注册到IoC容器中,refresh还剩下一个非常重要的方法,就是下面将要分析的:finishBeanFactoryInitialization,用以完成Bean创建、依赖注入和初始化等工作。
Reactor2020
2023/03/22
3200
【Spring源码】- 03 Spring IoC容器启动之Bean创建流程
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
程序员田同学
2022/03/09
7710
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
Spring加载流程源码分析03【refresh】
  前面两篇文章分析了super(parent)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码,
用户4919348
2019/04/02
1.2K0
Spring加载流程源码分析03【refresh】
深入理解-Spring-之源码剖析IOC(二)
我们刚刚创建了Bean工厂,并创建 BeanDefinitions 放进Map里,以beanName为key。那么我们现在有了Bean定义,但还没有实例,也没有构建Bean与Bean之间的依赖关系。
Bug开发工程师
2018/09/21
4350
深入理解-Spring-之源码剖析IOC(二)
Spring源码分析:bean加载流程
在Spring中,Bean的加载和管理是其核心功能之一,包括配置元数据解析、Bean定义注册、实例化、属性填充、初始化、后置处理器处理、完成创建和销毁等步骤。
后台技术汇
2024/10/14
1200
Spring源码分析:bean加载流程
这一次搞懂Spring的Bean实例化原理
前两篇文章分析了Spring XML和注解的解析原理,并将其封装为BeanDefinition对象存放到IOC容器中,而这些只是refresh方法中的其中一个步骤——obtainFreshBeanFactory,接下来就将围绕着这些BeanDefinition对象进行一系列的处理,如BeanDefinitionRegistryPostProcessor对象方法的调用、BeanFactoryPostProcessor对象方法的调用以及Bean实例的创建都离不开这些BeanDefinition对象。下面就来看看Spring是如何处理这些对象的。
夜勿语
2020/09/07
9010
Spring源码学习笔记(8)——Bean的生命周期
Bean的声明周期是指Bean从创建、初始化到销毁的整个过程。在Spring中,Bean的生命周期都是交给IoC容器管理的。Bean的主要生命周期主要有四个阶段:
张申傲
2020/09/03
2.4K0
深度解析 Spring Bean 的加载
这个方法首先从缓存中去获取,这个时候缓存中当然没有数据,因为此时是初始化,我们还没有将bean方到singletonObjects这个map中去,如下图代码。
用户1516716
2019/08/23
3850
(四)Spring源码解析:bean的加载流程
在前几讲中,我们着重的分析了Spring对xml配置文件的解析和注册过程。那么,本节内容,将会试图分析一下bean的加载过程。具体代码,如下图所示:
爪哇缪斯
2023/05/10
8300
(四)Spring源码解析:bean的加载流程
一文读懂 Spring Bean 的生命周期「建议收藏」
今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不知道跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不知道意思了,太绕了。
全栈程序员站长
2022/11/04
1.1K0
一文读懂 Spring Bean 的生命周期「建议收藏」
Spring IOC 源码解析(下)
上一步创建了BeanFactory,并将BeanDefinition注册到了BeanFactory中的ConcurrentHashMap中了。并且以BeanName为key,BeanFactory为value。那么我们现在有了Bean定义,但还没有实例,也没有构建Bean之间的依赖关系。我们知道,构建依赖关系是 IOC 的一个重要的任务,我们怎么能放过。那么是在哪里做的呢?在 finishBeanFactoryInitialization(beanFactory) 方法中,方法定义如下:
黑洞代码
2021/01/14
4090
Spring IOC 源码解析(下)
手撕spring bean的生命周期
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class)
程序员小强
2019/11/04
4860
Spring Bean 的生命周期
Spring是一个IOC容器框架,拥有DI依赖注入(Dependency Injection),DL依赖查找(Dependency Lookup)等功能。
兜兜毛毛
2021/04/28
4040
Spring Bean 的生命周期
InstantiationAwareBeanPostProcessor源码解析
文章目录 1. 简介 2. Bean加载顺序 3. InstantiationAwareBeanPostProcessor接口方法的执行顺序 4. 方法解析 5. 实例 6. 源码梳理 7. Autowired源码解析 8. 总结 简介 继承BeanPostProcessor接口,在此基础上又定义了三个方法,分别在Bean实例化前后【不是初始化】执行。 从上面的介绍可以看到,这个接口相对于BeanPostProcessor功能更加强大,一个接口承担了Bean的实例化前后、初始化前后责任。 Bean加载顺
爱撒谎的男孩
2019/12/31
1.1K0
推荐阅读
相关推荐
【Spring源码】讲讲Bean的生命周期
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档