深入了解Spring IoC

IoC全称Inversion of Control即控制反转,它还有一个别名依赖注入。spring利用Ioc容器帮我们自动构建对象及注入依赖对象,减少了对象构建与业务代码的耦合,使得我们能够更加高效愉快的写bug🐞了( ̄▽ ̄)"。接下来我们详细介绍下这个spring Ioc吧。

依赖注入原理

1.三种依赖注入方式

spring中有三种常见的依赖注入方式即:构造方法注入、setter方法注入、接口注入。其中前两种注入方式是我们现在仍然用比较多的,而最后一种由于其需要侵入代码,所以已经很少用了,这里就不介绍了。
(1) 构造方法注入:
这是我们经常能看到的注入方式,即通过对象构造器参数注入依赖对象。这种方式比较直观,同时构造完成后对象即进入就绪状态可以使用了。
(2)setter方法注入
在我们java bean对象中,经常会通过getter和setter方法获取和设置对象的属性,这些方法统称为setter方法,通过为依赖对象添加setter方法,容器就会帮我们实现依赖对象的注入。其中我们比较常用的@Autowired就属于这种方式。不过这种方式的注入,不能保证对象构造完成后就立马进入就绪状态。

在idea里当我们使用@Autowired时经常能看到"Filed injection is not recommended"的提示,告诉我们不推荐使用@Autowired进行注入。其主要原因是@Autowired的注入仅仅适用于Ioc容器,而当我们在程序中直接使用new去构造对象时,对象中的@Autowired依赖是无法自动注入的,就可能存在npe的风险。

2.BeanFactory和ApplicationContext

在讲两个主角之前,我们首先讲下IoC Service Provider。上面介绍了三种依赖的注入方式,但我们需要的是相应的角色或服务来帮我们实际的实现对象的构建和与依赖的注入,而IoC Service Provider就是这个角色。它是一个抽象的概念,可能是一段代码也可能是一组类,它主要负责业务对象的构建管理、业务对象间的依赖绑定。在spring中承担这个角色的主要就是BeanFactory和ApplicationContext,当然他们也承担着容器类的角色,我们这里只着重讲解他们作为IoC Service Provider的功能,他们作为容器的功能会在下面讲解。
这两个类中BeanFactory比较古老了,它默认采用延迟加载策略,即只有当需要访问容器中的受管对象时,才会对受管对象进行初始化及依赖注入操作,所以项目启动比较快。而ApplicationContext是目前项目中比较常用的,它继承了BeanFactory并增加了其他很多高级特性。ApplicationContext所管理的对象,默认在容器启动之后全部进行初始化和绑定操作,所以其启动速度会相对慢些,不过随着spring和java的不断优化和技术升级,这个启动时间一般都可以接受,而其提供的很多特性非常大的方便了我们的开发,所以目前我们大部分的项目都是使用的ApplicationContext。

3.依赖注入过程

上面已经介绍了我们常用的注入方式及帮我实现注入的角色,接下来我们就可以介绍下依赖注入的过程了🤡🤠。
(1)首先对容器来说,要实现依赖注入它最需要的是对象的信息及对象间的依赖关系,spring通常会通过XML或注解等方式记录这些信息。我们在一些比较古来的项目中还能看到这种XML配置文件,如下所示:

<bean id="djNewsProvider" class="..FXNewsProvider"> <property name="newsListener"><ref bean="djNewsListener"/></property><property name="newPersistener"><ref bean="djNewsPersister"/></property></bean>
<bean id="newsListener" class="..FXNewsProvider"> 
</bean>
<bean id="newPersistener" class="..FXNewsProvider"> 
</bean>

上面是我们在些老项目中常见的配置方式,在java5支持注解后,我们现在使用的更多是通过注解来代替这些XML配置。例如现在我们通过@Component、@Service等来标注对象信息,用@Autowired、@Resource来标注当前对象的依赖对象信息。ApplicationContext在项目启动时会通过我们配置的scanning-path自动的去寻找这些对象并解析保存其信息及依赖关系。
(2)spring收集到对象信息和依赖关系后会将这些信息封装到BeanDefinition中,每个容器中受管对象都会有一个BeanDefinition,它记录了对象的所有必信息。包括对应的Class类型、是否为抽象类、构造方法参数及依赖关系等。最后这些数据会被注册到BeanDefiniteRegistry中。
(3)当某个请求通过BeanFactory或ApplicationContext获取对象时(getBean()),就会开始对象的实例化了。对象在实例化的过程中会先获取对象的BeanDefinition,然后采用"策略模式",通过反射或者cglib动态代理来初始化对象(注意这里只是初始化,依赖对象还没注入)。
(4)在完成初始化后容器会通过BeanWrapper包裹住实例,然后通过BeanWrapper来实现对象属性值的设置和依赖的对象的注入。BeanWrapper根据实例的依赖对象到BeanFactory或ApplicationContext中获取相关对象,然后set进对象,这样就完成了实例的依赖注入。

我们可以再来看下(3)(4)的伪代码:

// 通过反射构建对象
Object provider = Class.forName("...Provider").newInstance();
Object listener = Class.forName("...Listener").newInstance();BeanWrapper newProvider = new BeanWrapperImpl(provider);
// 注入依赖对象
newProvider.setPropertyValue("listener", listener);

整个(1)(2)(3)(4)就是容器中依赖注入的详细流程了,是不是比想象中的要简单呢ヘ|・∀・|ノ*~●

Spring容器揭秘

IoC容器是Spring框架的重要组成部分。它通过加载配置数据并利用这些信息构造绑定容器内的所有对象,最终组装成一个可用的基于轻量级容器的应用系统。spring容器功能的实现主要分为两个过程:容器启动阶段Bean实例化阶段

1.容器启动阶段

上面也大致介绍过了,实际上容器的启动阶段主要就是通过某些特定的工具类来收集配置信息,并将解析后的信息封装为BeanDefinition,最后注册到相应的BeanDefinitionRegistry中,总的来说就是进行对象管理信息的收集。
在这个阶段spring为我们提供了一种叫BeanFactoryPostProcess的扩展机制来插手容器的启动,它可以对容器中的BeanDefinition进行修改,比如修改Bean定义的属性、为Bean增加其他信息等。
其中有一个使用的非常普遍的功能就是系统属性值的替换:我们经常可以在项目文件中看到properties文件,这里面经常会存放数据库密码账号等经常发生变化的配置数据,这些配置数据本来应该是在XML中配置的,但是我们通过${jdbc.url}这种方式将实际的数据配置在properties文件中。这个功能就是BeanFactoryPostProcess来帮我们实现,它修改了BeanDefinition的数据,用properties文件中数据替换了BeanDefinition中的${}内的数据。是不是很好玩🤣。

2.Bean实例化阶段

容器启动后并不会马上实例化Bean,而是需要等到客户端调用BeanFactory或ApplicationContext自动调用getBean方法获取对象时才会真正的实例化bean。我们可以先看下Bean实例化的过程。在这里插入图片描述
当客户端调用了getBean方法后,就是开始了Bean的整体的实例化流程。

  1. 首先第一步进行的是对象的初始化,这个阶段就是上面介绍过的通过cglib或者反射来初始化对象,这里只是初始化,依赖对象还没注入。
  2. 这阶段进行的是依赖对象的注入,依赖对象的注入我们在上面介绍过,主要是通过BeanWrapper来实现的。
  3. 在之后会检查Aware相关接口并设置相关依赖,比如Bean名称的设置,以及我们比较常用的ApplicationContextAware,这个Aware接口可以将容器类ApplicationContext的引用注入对象中,这样我们就可以方便的通过ApplicationContext.getBean()获取我们需要的对象了。
  4. 接下来是重要的BeanPostProcessor了,它分为Pre和Post两种,在图中可以看到,这两个processor分别处在Bean实例化(调用构造方法)前和实例化(调用构造方法)后,它类似容器启动阶段的BeanFactoryPostProcessor可以用来动态修改扩展Bean的数据信息。其中一个最重要的应用就是Spring Aop的动态代理,Aop的动态代理就是通过BeanPostProcessor来实现的,它利用反射或cglib通过BeanPostProcessor生成了实例的代理对象并直接返回给了调用方完成了动态代理。
  5. 接下来在两个BeanPostProcessor间,对象实例化(调用构造方法)前,spring会检查对像是否实现了InitializingBean接口,如果是就会调用afterPropertiesSet()方法进一步调整对象状态,除此之外我们常用的 @Bean(initMethod = “func”)中initMethod方法的执行也是在这里实现的。
  6. 最后阶段就是在容器关闭Bean生命周期结束时,检查Bean实现的destory-method,其中 @Bean(destroyMethod = “func”)中的destroyMethod方法也是在这里执行的。

好了,至此我们们就把容器的启动及Bean的实例化讲完了,舒服了🍑🍓🍎🍐。下面在简单讲下Bean的生命周期。

3.Bean的生命周期

容器除了会帮我们进行依赖注入外还会帮我们管理对象的生命周期,Bean的生命周期被称为Scope,可以理解为对象的存活范围或存活时间。bean的scope最常见的主要有两种:singleton和prototype。
(1)singleton:
这种类型是我们最常见的。被标记为singleton scope的对象在容器中只存在一个实例,所有对该对象的引用都共享这个实例,它会一直存活到容器退出,几乎和容器的生命周期一样长。
(1)prototype:
被标记为prototype scope的对象,在容器每次收到该对象的请求时,都会生产一个新的对象返回给请求方,并且放回给请求方后,容器就不会再拥有当前对象的引用,请求方需要自己负责该对象的生命周期。这种类型很像我们自己在程序中使用new生成的对象,这中对象在我们不在使用后,会被GC线程在何时的时机回收掉,结束掉他的一生。

除此之外还有些不太常见的类型,原理都是一样的,这里就不在赘述了。终于写完了,嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻,打完收工,吃饭去🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450947.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软文营销实战记录

最近拜读了徐茂权老师的《 网络营销决胜武器(第2版)》&#xff0c;下面会梳理书中的内容&#xff0c;记录下以后可能会用到的软文营销的技巧。 一、软文载体 1、平面媒体软文&#xff1a;报纸、期刊。 2、非正式出版的基于印刷、打印形式载体的软文&#xff1a;企业印刷的宣传册…

oracle中rownum和row_number()的区别

见&#xff1a;http://www.jb51.net/article/65960.htm row_number()over(partition by col1 order by col2)表示根据col1分组&#xff0c;在分组内部根据col2排序&#xff0c;而此函数计算的值就表示每组内部排序后的顺序编号&#xff08;组内连续的唯一的&#xff09;。 与ro…

java类加载顺序

在java中类的加载、初始化都是在程序运行期完成的&#xff0c;虽然会稍微增加开销&#xff0c;但是却很大的增加了灵活性&#xff0c;我们可用在运行期间动态的去网络或其他地方加载一个二进制流来作为程序代码的一部分。接下来我们简单介绍下java类加载过程。 从上图中我们可…

dealloc不调用的情况

2019独角兽企业重金招聘Python工程师标准>>> 1、没有停止定时器 - (void)dealloc { [_timer invalidate]; _timer nil; } 2、VC中有代理Delegate&#xff0c;需要设置delegate的时候&#xff0c;设置为weak property (nonatomic,weak) id<ZoeEatDe…

day10-列表生成式

列表生成式即List Comprehensions&#xff0c;是Python内置的非常简单却强大的可以用来创建list的生成式。 1、生成一个列表 a [i for i in range(1,100) if i%21]print(list(a))或print(a)[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, …

jrebel、JavaRebel

见&#xff1a;https://baike.baidu.com/item/jrebel/1115725?fraladdin JRebel是一套JavaEE开发工具。中文名jrebel属 性JavaEE开发工具资 费收费软件作 用Jrebel 可快速实现热部署JRebel是一套JavaEE开发工具。JRebel允许开发团队在有限的时间内完成更多的任务修正…

自己写函数库

大家现在写 程序&#xff0c;是不是都是用新唐提供的函数库&#xff1f;在体验 开发板的一开始&#xff0c;我也是使用函数库&#xff0c;毕竟这个太方便了。可是有一天&#xff0c;我发现一个只使用时钟和IO以及 调试 串口的程序居然查过了16k的时候&#xff0c;我震惊了&…

[MicroPython]stm32f407控制DS18B20检测温度

2019独角兽企业重金招聘Python工程师标准>>> 1.实验目的 1. 学习在PC机系统中扩展简单I/O 接口的方法。 2. 进一步学习编制数据输出程序的设计方法。 3. 学习DS18B20的接线方法&#xff0c;并利用DS18B20检测当前温度。 2.所需元器件 F407Micropython开发板…

带你理解Spring AOP

AOP概述 在我们的日常开发中&#xff0c;除了正常业务逻辑外&#xff0c;还可能经常会需要在业务逻辑的特定位置加入日志&#xff0c;以便于调试和问题分析。但是这种插入日志的逻辑和业务逻辑间并不存在连续性和依赖性&#xff0c;这种逻辑侵入随着项目的不断发展&#xff0c…

10.20随笔

ES6 ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association&#xff09;通过ECMA-262标准化的脚本程序设计语言。 这种语言在万维网上应用广泛&#xff0c;它往往被称为JavaScript或JScript&#xff0c;但…

极客招募令!兄弟杯区块链极客竞技大赛在上海等您来战!

据悉&#xff0c;由国内首家区块链技术社区区块链兄弟主办&#xff0c;旺链科技、离子链、中国云体系产业创新战略联盟、无退社区、指旺金科等单位强力支持&#xff0c;HiBlock区块链社区、火球财经、布洛克财经、海豚区块链、区块网等百家技术社区和媒体通力合作的兄弟杯区块链…

Java中Web程序修改配置文件不重启服务器的方法

见&#xff1a;http://blog.sina.com.cn/s/blog_69398ed9010191jg.html 另&#xff1a;http://ekisstherain.iteye.com/blog/1701463 jrebel 、JavaRebel是什么&#xff0c;见另一博客&#xff1a;jrebel/JavaRebel 开发环境 1. JDK 2. MyEclipse 3. Tomcat 4. Struts2 5.…

ffmpeg-0.6.3 移植到 windows 开源代码

ffmpeg-0.6.3开源编码解码库&#xff0c;从linux下移植到windows vs2005&#xff0c;全部开源。 需要 Intel C Compile 和 开源的SDL库支持&#xff0c;由于 Intel C Compile支持C99语法&#xff0c;所以源代码改动很小很小。 主要的修改 1&#xff1a;添加了linux中有而wind…

一起唠唠分布式锁

&#xff08;1&#xff09;分布式锁和分布式事务的区别 1.分布式锁是在集群环境下&#xff0c;用来控制不同机器对全局共享资源的访问。 2.分布式事务是在集群环境下&#xff0c;用来保证全局事务的一致性&#xff0c;保证多个数据库的数据整体上能正确的从一个一致性状态转到…

luogu2577/bzoj1899 午餐 (贪心+dp)

首先&#xff0c;应该尽量让吃饭慢的排在前面&#xff0c;先按这个排个序 然后再来决定每个人到底去哪边 设f[i][j]是做到了第i个人&#xff0c;然后1号窗口目前的总排队时间是j&#xff0c;目前的最大总时间 有这个i和j的话&#xff0c;再预处理出前i个人的排队总时间sum[i]&a…

wpf中xps文档合并功能实现

原文:wpf中xps文档合并功能实现跟着上一篇的xps文档套打的文章&#xff0c;近期一直在研究xps打印技术&#xff0c;其中用户提到了一个需求&#xff0c;要求能够多页面进行打印&#xff0c;我的想法是&#xff0c;先生成xps文件&#xff0c;然后将文件读取出来以后&#xff0c;…

DCT(离散余弦变换(DiscreteCosineTransform))

离散余弦变换&#xff08;Discrete Cosine Transform&#xff0c;简称DCT变换&#xff09;是一种与傅立叶变换紧密相关的数学运算。在傅立叶级数展开式中&#xff0c;如果被展开的函数是实偶函数&#xff0c;那么其傅立叶级数中只包含余弦项&#xff0c;再将其离散化可导出余弦…

从源码看ConcurrentHashMap

简介 ConcurrentHashMap是线程安全的HashMap实现&#xff0c;这里主要研究JDK8后的ConcurrentHashMap&#xff0c;下面是ConcurrentHashMap的简单结构&#xff1a; ConcurrentHashMap基于HashMap的基本逻辑&#xff0c;通过CAS synchronized 来保证并发安全性。ConcurrentHas…

代码重构的方法

见&#xff1a;http://blog.csdn.net/u011889786/article/details/51865344 见&#xff1a;http://blog.csdn.net/weiky626/article/details/1602691 一.提取子函数 说白了就是一个大函数里&#xff0c;可以根据不同功能分成几个小函数&#xff0c;因为说不定&#xff0c;其他…

android 去掉标题栏、状态栏、横屏

// 去掉标题栏supportRequestWindowFeature(Window.FEATURE_NO_TITLE);// 全屏、隐藏状态栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 横屏setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION…