引言

spring加载xml的对象信息解析实例化成各个bean的过程我在这里就不细讲了,毕竟从头开始看很容易绕晕大家,反而让大家觉得这并不需要写。我们姑且认为spring已经加载好了各类对象信息封装成BeanDefinition,并已经实例化存储在了某个地方。不管是懒汉还是饿汉,都要经历反射出对象实例,然后初始化,我们先从spring中比较好理解的地方来入手IOC,那就是IOC中的bean在实例化之后的初始化操作。主要涉及到BeanPostProcessor,InitializingBean这两个的应用。

IOC的bean初始化

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}invokeInitMethods(beanName, wrappedBean, mbd);if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}
复制代码

为了方便阅读,我删除了部分不相干的代码。 AbstractAutowireCapableBeanFactory#initializeBean就是初始化IOC容器中的Bean的主要方法,从这个方法入手,来看看IOC加载Bean到底做了什么? 将实例化好的bean传入该方法:

--> 调用BeanPostProcessor的postProcessBeforeInitialization方法
--> 调用bean实例的初始化方法
--> 调用BeanPostProcessor的postProcessAfterInitialization方法
复制代码

以下三段代码就是这三个过程:

@Overridepublic Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessBeforeInitialization(result, beanName);if (result == null) {return result;}}return result;}
复制代码

循环所有实现了BeanPostProcessor类的bean,并执行相应的对象初始化之前的方法postProcessBeforeInitialization

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)throws Throwable {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>() {@Overridepublic Object run() throws Exception {((InitializingBean) bean).afterPropertiesSet();return null;}}, getAccessControlContext());}catch (PrivilegedActionException pae) {throw pae.getException();}}else {((InitializingBean) bean).afterPropertiesSet();}}if (mbd != null) {String initMethodName = mbd.getInitMethodName();if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {invokeCustomInitMethod(beanName, bean, mbd);}}}
复制代码

该段代码给一些实现了InitializingBean的bean进行初始化操作。

注意:

1:spring为bean提供了两种初始化的方式,需要实现InitializingBean接口,重写afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用,但是会先执行afterPropertiesSet再执行init-method。下文还有demo证明。

2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

@Overridepublic Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessAfterInitialization(result, beanName);if (result == null) {return result;}}return result;}
复制代码

再次循环所有实现了BeanPostProcessor类的bean,并执行相应的对象初始化之前的方法postProcessAfterInitialization。顺便提一下,以后要写的Spring AOP的底层处理也是通过实现BeanPostProcessor来执行代理包装逻辑的。

IOC的bean注入

我们的ioc对象初始化好了,接下来就要看看最关键的依赖注入了。先思考一个问题,spring在什么时候把对象注入进去的?这里先不解释,我们看这段源码:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {if (pvs instanceof MutablePropertyValues) {...}else {original = Arrays.asList(pvs.getPropertyValues());}TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);// Create a deep copy, resolving any references for values.List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());boolean resolveNecessary = false;for (PropertyValue pv : original) {if (pv.isConverted()) {deepCopy.add(pv);}else {String propertyName = pv.getName();Object originalValue = pv.getValue();//在这里解析并塞入了注入的对象Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);Object convertedValue = resolvedValue;boolean convertible = bw.isWritableProperty(propertyName) &&!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);}// Possibly store converted value in merged bean definition,// in order to avoid re-conversion for every created bean instance.if (resolvedValue == originalValue) {if (convertible) {pv.setConvertedValue(convertedValue);}deepCopy.add(pv);}else if (convertible && originalValue instanceof TypedStringValue &&!((TypedStringValue) originalValue).isDynamic() &&!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);}else {resolveNecessary = true;deepCopy.add(new PropertyValue(pv, convertedValue));}}}}
复制代码

我还是删了很多其他逻辑,这里主要的一句话就是Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); 在这里分析并塞入了注入对象的关联。具体怎么操作还得看自己去翻阅。接下来我用最简化的代码方式来展示我的寻找过程:

AbstractBeanFactory类
@Override
public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);
}
复制代码

我们肯定找到拿到每个bean的如果,如果不存在就会创建。

AbstractBeanFactory类
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}
复制代码

然后再进入createBean(),它的实现是在 AbstractAutowireCapableBeanFactory 当中:

AbstractAutowireCapableBeanFactory类
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {Object beanInstance = doCreateBean(beanName, mbdToUse, args);return beanInstance;
}
复制代码
AbstractAutowireCapableBeanFactory类
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}}
}
复制代码

重点关注 createBeanInstance() 和 populateBean() 这两个方法。其中,createBeanInstance方法生成了Bean所包含的Java对象:

AbstractAutowireCapableBeanFactory类
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {if (resolved) {if (autowireNecessary) {return autowireConstructor(beanName, mbd, null, null);}else {return instantiateBean(beanName, mbd);}}
}
复制代码
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;
}
复制代码

重点关注 getInstantiationStrategy()这个方法,可以看到instantiateBean方法的功能实现是通过调用getInstantiationStrategy().instantiate方法实现的。 getInstantiationStrategy方法的作用是获得实例化的策略对象,也就是指通过哪种方案进行实例化的过程。继续跟踪下去我们可以发现,Spring当中提供了两种实例化方案: BeanUtils和Cglib方式。BeanUtils实现机制是通过Java的反射机制,Cglib是一个第三方类库采用的是一种字节码加强方式机制。Spring中采用的默认实例化策略是Cglib。

接下来就是重头戏建立bean的依赖关系了。我们回到doCreateBean方法中的populateBean(beanName, mbd, instanceWrapper);

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {applyPropertyValues(beanName, mbd, bw, pvs);
}
复制代码

有没有发现,我们已经来到我开始讲的applyPropertyValues方法了? 下面我们就不看源码了,我们用反射来把value注入进去,这样更容易理解。

try {Method declaredMethod = bean.getClass().getDeclaredMethod("set" + propertyValue.getName().substring(0, 1).toUpperCase()+ propertyValue.getName().substring(1), value.getClass());declaredMethod.setAccessible(true);declaredMethod.invoke(bean, value);
} catch (NoSuchMethodException e) {Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());declaredField.setAccessible(true);declaredField.set(bean, value);
}
复制代码

拿到set参数的方法,如果出现异常,表示并没有写set方法,则就粗暴的方法塞入field。(此处并不是源码,是我根据源码理解写的比较通俗易懂的方式)

这是我觉得在繁多的spring源码中找出一段目标代码先看到,更容易让人能够跟着思路走下去。

题外话:

synchronized (this.dependenciesForBeanMap) {Set<String> dependenciesForBean =this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));dependenciesForBean.add(canonicalName);}
复制代码

在看IOC源码的时候看到Map的computeIfAbsent让我恍然大悟,我来用以前怎么写这段代码的来解释这段代码什么意思如下:

Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean != null){dependenciesForBean = new LinkedHashSet<>(8);
}
dependenciesForBean.add(dependentBeanName);
dependenciesForBean.add(canonicalName);复制代码

这是jdk1.8才支持的lambda写法,是不是代码更简洁?

再来分享一种简洁的方法,计算每个学生的总分记录到map中:

List<Student> students = new ArrayList<>();
students.add(new Student("张三", "语文", 18));
students.add(new Student("张三", "数学", 20));
Map<String, Integer> resultMap = new HashMap<>();
for (Student student : students) {resultMap.merge(student.getName(), student.getScore(), (a, b) -> b + a);
}
复制代码

Spring Ioc之初始化相关推荐

  1. Spring IoC容器初始化源码(1)—容器初始化入口以及setConfigLocations设置容器配置信息【一万字】

      基于最新Spring 5.x,对于基于XML的Spring IoC容器初始化过程中的setConfigLocations设置容器配置信息方法的源码进行了详细分析,最后给出了比较详细的方法调用时序图 ...

  2. 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  3. 面试被问烂的 Spring IOC(求求你别再问了)

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 作者:莫那·鲁道 链接:http://thinkinjava.cn 广义的 IOC IoC(Inv ...

  4. Spring IOC流程源码分析

    一.Spring 核心容器 IOC初始化过程 Spring 核心之 IOC 容器初体验 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里 ...

  5. java 从一个容器获取对象,如何从 Spring IoC 容器中获取对象?

    前面几篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注册我们定义的 bean 信息. 其中,「Spring 中的 IoC 容器」对 Spring 中的容器做了一个概述,「Sprin ...

  6. JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(6):Spring IOC容器学习(概念、作用、Bean生命周期)...

    一.IOC控制反转概念 控制反转(IOC)是一种通过描述(在Java中可以是XML或者是注解)并通过第三方去生产或获取特定对象的方式. 主动创建模式,责任在于开发者,而在被动模式下,责任归于Ioc容器 ...

  7. Spring IOC知识点一网打尽!

    前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总 ...

  8. 深入理解Spring IoC的原理(转发)

    前言 本文转发自好好学java,作者:莫那·鲁道(点击蓝色字体即可跳转),本文有所修删改. 原作者的这篇文章对Spring IoC的实现原理讲的挺详细的.当然,也正因为如此,所以理解起来有些难度,估计 ...

  9. 关于spring IoC 学习

    What is IoC 简单来说: 控制:当前对象对其内部成员对象的控制权/获取组装对象的过程 反转:上述的过程/控制权,交由专门的第三方组件(容器或者说平台)来管理 这种从具体对象手中,交出控制的做 ...

  10. spring源码 — 一、IoC容器初始化

    IoC容器初始化 注意:本次的spring源码是基于3.1.1.release版本 容器:具有获取Bean功能--这是最基本功能,也是BeanFactory接口定义的主要行为,在添加了对于资源的支持之 ...

最新文章

  1. 街电与搜电网络营销外包合并,怪兽充电终上市共享充电市场将迎来新变化
  2. 前后端分离项目部署(服务器或本地)
  3. mybatis中的智能标签之二
  4. android queue用法,GitHub - rygz146/TQueue: Android 可以任意切换线程的任务队列, TQueue
  5. 最短路弗洛伊德(Floyd)算法加保存路径
  6. h5滚动隐藏滚动条_这 10 个值得开启的隐藏功能,让你的 Chrome 释放更多潜力
  7. 【高等数学】一元函数微分学
  8. oracle取消备份存放本地,Oracle自动备份,压缩打包,删除原文件
  9. 项目管理工具project软件学习(五) - 创建WBS、设置任务依赖关系
  10. 安全运维 - Windows系统应急响应
  11. TextWatcher实现一键清空EditText
  12. linux arm 物理内存,linux – 如何在Arm Architecture硬件上进行内存测试? (像Memtest86这样)...
  13. solaris java 安装_solaris中安装jdk环境
  14. 3.14 我总结的一些小红书笔记发布频率技巧!【玩赚小红书】
  15. 二极管、三极管、MOSFET管知识点总结
  16. 【机器学习】22个开源的机器学习库,帮助您选择一个适合您的管道工具。
  17. 基于奇异值分解的图像压缩matlab
  18. 20岁跟对人,30岁做对事(三)
  19. 电阻 电容表 电感表 频率表 测量套件 51单片机 STC89C52 程序 万用表
  20. 睡眠好坏 枕头是关键!

热门文章

  1. Apache Flink 零基础入门(八): SQL 编程实践
  2. 朋友圈疯转的“佩奇”是啥?用 Python 画个小猪佩奇来告诉你
  3. 计算机文化基础清华大学出版社,清华大学出版社-图书详情-《计算机基础》
  4. PHP-----strpos() 函数的用法
  5. 城市轨道交通运营管理属于什么院系_2020年报考山东交通职业学院城市轨道交通运营管理专业怎么样...
  6. vivado中交织模块_Adalm Pluto SDR主动学习模块让您拥有完善的无线电RF射频实验室...
  7. 徐州计算机专业技校,2021徐州所有的中专技校职高排名
  8. python中替换字符串中子串的函数为_python替换字符串中的子串图文步骤
  9. 刷新报表页面的方法总结
  10. 请求的资源不可用html,“HTTP状态404请求的资源不可用”