spring源码分析系列(一)
本系列以官方开发文档+spring5的源码为主
一、IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有其他方式叫“依赖查找”(Dependency Lookup)、推拽。(依赖注入DI是IOC的一种实现方式)
控制反转,简单点说,就是创建对象的控制权,被反转到了Spring框架上。通常,我们实例化一个对象时,都是使用类的构造方法来new一个对象,这个过程是由我们自己来控制的,而控制反转就把new对象的工作交给了Spring容器。
那么依赖查找和依赖注入有什么区别呢?
依赖查找,主要是容器为组件提供一个回调接口和上下文环境。这样一来,组件就必须自己使用容器提供的API来查找资源和协作对象,控制反转仅体现在那些回调方法上,容器调用这些回调方法,从而应用代码获取到资源。
依赖注入,组件不做定位查询,只提供标准的Java方法让容器去决定依赖关系。容器全权负责组件的装配,把符合依赖关系的对象通过Java Bean属性或构造方法传递给需要的对象。
IoC容器:具有依赖注入功能的容器,可以创建对象的容器。IoC容器负责实例化、定位、配置应用程序中的对象并建立这些对象之间的依赖。(IOC由于大部分是单例模式,所以可以提高对象的复用性)那IOC和DI是什么关系?简单点讲IOC就是讲对象的控制权转交给容器的一种思想,而DI是对这种思想的实现技术。
1、依赖注入是什么?
就拿我们开发中常用的例子好了
@Controller public void A(){//A类需要用到b对象,此时A依赖于b。@Autowiredprivate B b; }
看看上面的例子:此时就可以说A依赖于B。而获取对象b就是所谓的依赖注入(容器将管理的bean对象注入到代码中)
(这里依赖注入的装配采用的是自动装配,自动装配不等于依赖注入)
我们先来看对象注册到容器再到被注入的过程:
- 声明相关的类/对象信息及依赖关系,然后将对象和对应的依赖关系等注册到容器中(通过xml、注解、java cofiguration方式)
- 容器拿到依赖关系后会创建、维护具有对应依赖关系的bean
- 当程序需要的时候,此时可以用从容器中获取得到对应的bean(容器注入给我们对应的对象)
而依赖注入就相当于容器将我们所需要的资源动态注入给b对象。
(其中IOC就是一种思想,不用在自己每次要用对象时去手动new一个对象,将对像的控制权交给容器,而在你需要时候容器会将对应的bean注入给你)下面就图中的三个步骤作分析:
spring编程的风格
- schemal-based-------xml
- annotation-based-----annotation 注解
- java-based----java Configuration
先说第3个步骤:依赖注入
Spring的依赖注入的方式主要有三个:(所以面试时勇敢的跟面试官说两种)
- 接口方式(spring3之后不支持了)
- set注入方式
- 构造器注入方式
(下面的说明以xml配置为例)(注意:如果引用依赖的是对象等用ref属性,如果是数据类型等用value属性)
第一个步骤:定义类/对象、set/构造方法(用于后面的依赖注入)、依赖关系等注册到容器当中:
(这里可用p:、c:命名去分别代表set注入和构造注入)
(1)、先来说说set方法:(官方文档
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-setter-injection)
(先定义对应的类/对象信息set方法,再去xml声明对应的依赖)(注意:这里的标签用property表示set方法注入)
(2)构造方法注入(官方文档https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-constructor-injection)
(类似,定义类/对象和构造方法、再去xml中配置依赖关系等)(注意:这里的标签用constructor表示构造方法注入)
第二个步骤:容器根据注册的信息等创建并维护相应的bean
这里不进行详细说明,具体实现看后面源码分析。
第三个步骤:
通过相应的set/构造方法将对应的bean注入给需要的对象(这里也具体看后面源码)
当然注入也可以是其他数据类型的注入:map、list、set等等,具体自己去找找。
上面讲的是xml的配置,要是嫌弃xml太麻烦怎么办?那下面讲讲注解方式:
先讲个xml的例子:
//1、写下类/对象信息 Class A {set/构造方法;test(); }Class B {private A a;a.test(); }//2、再去xml配置bean和对应依赖关系 <bean id="B" class="类路径"><propret id="A" ref="A"/> </bean><bean id="A" class="类路径"/>
从xml的配置例子可以看出容器用set/构造方法就依赖注入给对象a
在看看配置方式的例子:
//1、用注解先注册到容器 @Service Class A{}@Controller Class B {@Autowired //注解自动装配private A a; }//2、配置配置类 (java-based----java Configuration风格) @configuration //声明这是一个配置类 @ComponentScan(“扫描包路径”) //扫描包 public void AppConfig{ }
观察一下,注解配置和xml配置有什么不同?
1、没写set/构造方法 2、没写xml的配置 3、依赖注入时多了一个@Autowired注解
对于第一个问题详细看:https://blog.csdn.net/qq_19782019/article/details/85038081
对于2、3问题统一回答:即spring提供了一种叫自动装配的功能。
对于自动装配和依赖注入的关系详细看:https://www.cnblogs.com/zhuwoyao88/p/6596295.html
依赖注入:当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。然而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IoC容器来完成,然后注入调用者,因此也称为依赖注入。依赖注入的方式有两种:构造器注入和setter注入。
装配:创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化。这就是装配。
自动装配:开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。与自动装配配合的还有“自动检测”,这个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依赖注入“自动化”的一个简化配置的操作。
依赖注入与装配的关系:依赖注入的本质就是装配,装配是依赖注入的具体行为。
( 首先,确定一下装配的概念。《spring实战》中给装配下了一个定义:创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化。这就是装配。如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。与自动装配配合的还有“自动检测”,这 个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依赖注入“自动化”的一个简化配置的操作。
)简单的说:控制反转是一种思想,而依赖注入是这个思想的一种实现技术方式。而这个实现技术的方式(DI)的本质就是要去装配调用者需要的bean,而自动装配就是说将装配的过程“自动化”。
那么下面就讲讲自动装配的内容:
自动装配?
上面说过,IOC的注入有两个地方需要提供依赖关系,一是类的定义中,二是在spring的配置中需要去描述。自动装配则把第二个取消了,即我们仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入。
在实际开发中,描述类之间的依赖关系通常是大篇幅的,如果使用自动装配则省去了很多配置,并且如果对象的依赖发生更新我们可以不需要去更新配置,但是也带来了一定的缺点。
也就是看:xml例子中
而在注解的例子中:
也就是说自动装配帮我们减少了第二次定义的步骤(减少了xml配置对依赖关系的配置)。
(注意如果注解只是相当于xml中定义一个对象的<bean>,都是向容器注册对象bean,而还有各个对象的依赖关系还没有给到容器,而自动装配则帮我们少了依赖关系的第二次配置)
那么自动装配有几种方式?
- no:(默认值),不采用自动装配。
- byName:容器将查找与属性名相同的Bean,然后自动注入到该属性中,如果没有找到,则该属性将不会被注入。当然,使用自动装配时,也可以显式地注入Bean 的部分属性。
- byType:注入过程与byName 类似, 不同的是,容器查找的是与属性的设值方法参数类型兼容的Bean。
- constructor:容器将试图找出所有与构造方法的参数类型兼容的Bean ,然后确定某个合适的构造方法。如果没有符合调用任何构造方法所需的Bean,则容器将抛出UnsatisfiedDependencyException。
- autodetect:让容器全面自动判断
知道了什么是自动装配,那下面就开始说说是怎么设置这些自动装配的方式的:
这里分xml和注解方式的:xml:
1、全局自动装配类型指定:default-autowire="byName/byType等等"
2、局部bean的自动装配指定:
注解方式:又得分两种:一种是spring提供的,一种是javaee提供的
(1)spring提供的:
@Autowired:byType类型,但是这个属性是强制性的,也就是说必须得装配上,如果没有找到合适的bean能够装配上, 就会抛出异常。这时可以使用required=false来允许可以不被装配上,默认值为true。当required=true时, @Autowired要求必须装配,但是在没有bean能装配上时,就会抛出异常NoSuchBeanDefinitionException, 如果required=false时,则不会抛出异常。另一种情况是同时有多个bean是一个类型的,也会抛出这个异 常。此时需要进一步明确要装配哪一个Bean,这时可以组合使用@Qualifier注解,值为Bean的名字即可。
@Qualifier:使用byName进行装配,这样可以在多个类型一样的bean中,明确使用哪一个名字的bean来进行装配。 @Qualifier注解起到了缩小自动装配候选bean的范围的作用。
(2)javaee提供的:
@Inject:与@Autowired注解作用一样,也是byType类型,而且是Java ee提供的,完全可以代替@Autowired注解,但是 @Inject必须是强制装配的,没有required属性,也就是不能为null,如果不存在匹配的bean,会抛出异常。 @Autowired@Qualifier可以组合使用,@Inject也有一个组合的注解,就是@Named注解。
@Named:@Qualifier作用一样,也是byName,但是不是spring的,是java ee标准的。这样就出现了两套自动装配的注 解 组合,@Autowired与@Qualifier是spring提供的,@Inject与@Named是Java EE的。但是@Qualifier注解在 java ee中也有一样,作用与spring的@Qualifier注解一模一样,只是所在的包不一样。不过建议大家使用spring 的。
@Resouce:这个注解也是Java ee的,也是byName类型的,原理同@Qualifier和@Named是一样的。那下面就讲讲怎么用吧:
先来讲讲一个对象交给容器后bean的名字是怎样的设置的?(在没有指定bean名,使用默认的情况下)
xml方式: Class A{private B bb;setBdao(B bb){this.dao=bb;}}xml中:name属性是指定bean的名字 <bean id="bdao" name="bdao" class="路径"> 如果此时没有name属性,则回去类中找到set方法名:即setBdao,然后拿到除set后第一个字母小写即bdao 所以在默认情况下bean的名字与set方法的名字有关,和private B bb;中的bb没关 (对于另一种构造方法注入的这里不讲)注解方式:@Compent //注册到容器 Class Adao{} //这里如果没有指定bean名字,则会以类名第一个字母小写为bean名,即bean名为adao。
当然你也可以继承某个类去自己写你的命名规则
上面讲的是给bean设置名字,下面讲的是给对象装配什么bean
先来个例子:
1、@Autowired(byType)
Interface Adao{}@Compent(“dao1”) //注册到容器(并设置该bean名字为dao1)(当然bean名默认是类名第一个字母小写) Class B impls Adao{} @Compent(“dao2”) //注册到容器 Class C impls Adao{}此时按byType方式自动装配 @Autowired private Adao adao;
抱歉这里要报错了,因为有两个实现类都实现接口Adao并且都注册到容器中去了,此时容器根据类型去给adao对象装配一个bean,但此时发现容器中有两个同样类型的bean。所以就报错咯。
那么怎么解决?
这就需要@Autowired的搭档@Qualifier。已知该注解可以明确使用哪一个名字的bean来进行装配
@Autowired //这里找到了同类型的dao1、dao2 @Qualifier(“dao1”)//这里再去找名字为dao1的bean最后注入到adao对象中 private Adao adao;
2、@Inject与@Named这个就不说了,自己去找找看。下面说说@Resouce注解
大家都知道@Resouce是按照byName的方式去装配bean,那当你就只有@Resouce,而没有去告诉容器你要装配哪个name的bean时(即@Resouce(name)name属性没有指定要装配哪个bean),这时容器怎么办?
@resource //不告诉我容器要注入哪个名字的bean private Adao adao;这时容器就会去找一个名为adao的bean装配给adao对象,也就是说byName情况下默认装配的是属性对象名。 (这里和set的方法无关)
当然你也可以指定对应name的bean:@resource(name=“要装配的bean名”)4
3、对于@Autowired是byType的,1中解答了找到两个同类型的bean怎么解决?那你有没有想过,要是一个都没找到呢?
注意:当@Autowired装配bean时如果没有找到bean,此时就会将byType转换为byName再去查找一遍(byName的问题在2中也说了),两种方式都没找到,那才会报错。
懒加载
即spring在去容器中get的时候容器才去创建bean(一种懒加载思想)
@Lazy默认是开启的true ,可以关掉@Lazy(“false”)
springbean的作用域
先说说单例和原生(即多例)
sington:单例(默认是单例),则多次去容器获取的bean都是同一个(对象复用性)。
prototype:可以在类前加上@scop(“prototype”),则每次去获取bean都会去重新创建一个bean。
那么在单例中依赖多例对象会引发一个问题:Singleton Beans with Prototype-bean Dependencies,??什么东西?
Class A{ //默认是单例@Autowiredprivate B b; }@Scop(“prototype”) //多例模式 Class B{}这里就是所谓的Singleton Beans with Prototype-bean Dependencies问题。 简单点讲就是一个单例对象依赖于一个多例对象,而由于单例对象A只创建初始化一次,在这一次中也会帮多例对象B创建初始化一次。而如果再去获取A时,由于是单例所以不会再去创建,那么也就导致其中依赖的B对象也不会去再次创建。原则上我们是希望B对象每次获取的对象都是不同的,但由于A是单例导致无法每次去帮B创建对象。所以这就是所谓的Singleton Beans with Prototype-bean Dependencies问题
简单点讲就是prototype失效了
那么怎么解决这个问题?
那就不用A去帮你了,让B自己去找:
Class abstract A{ //默认是单例@Lookup(“bean的名字”) //自己去请求容器注入public abstract B getB(); }@Scop(“prototype”) //多例模式 Class B{}
spring bean的生命周期和回调
1、生命周期:后面源码讲
2、生命周期的回调: Lifecycle Callbacks
官网:
这里主要介绍Initialization Callbacks(初始化后调用)和Destruction Callbacks(销毁前调用)
1、Initialization Callbacks的实现()
有多种方式。
(1)可以是实现接口InitializingBean重新写afterPropertiesSet()方法
(2)推荐使用,不会依赖spring的api,减少和spring的耦合
xml方式:
用xml的bean用属性init-method=“方法名如init”然后再去该类中写该方法如init(官网例子)
注解方式:再init方法加上
@PostConstruct注解则表示该方法是
Initialization Callbacks回调方法即可public class ExampleBean {@PostConstructpublic void init() {// do some initialization work} }
2、Destruction Callbacks的实现
(1)实现DisposableBean接口再去重写destroy()方法
(2)推荐使用,不会依赖spring的api,减少和spring的耦合
xml:官网例子:
注解方式:
public class ExampleBean {@PreDestroypublic void cleanup() {// do some destruction work (like releasing pooled connections)} }
上面基本讲官方文档的一些比较重要的IOC核心知识讲了一下。那下面就说说一些其他的注解使用吧
直接看我对一些注解的总结和说明:spring常用注解说明
spring源码分析系列(一)相关推荐
- Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean
前言 spring创建bean的方式 测试代码准备 createBeanInstance()方法分析 instantiateUsingFactoryMethod()方法分析 总结 spring创建be ...
- Spring源码分析系列-Bean的生命周期(总结篇)
ApplicationContext和BeanFactory BeanFactory是Spring中的顶层接口,只负责管理bean,而ApplicationContext也实现了BeanFacto ...
- Spring源码分析系列-循环依赖和三级缓存
目录 循环依赖 多级缓存 一级缓存 二级缓存 当循环依赖遇上AOP 三级缓存 Spring三级缓存源码实现 总结 循环依赖 BeanFactory作为bean工厂管理我们的单例bean,那么肯定需 ...
- spring源码分析系列(二)AOP应用
这里讲AOP应用 先来说说一个比较虚无缥缈的问题:什么是AOP? 1.OOP 在说AOP之前,我们需要先看看什么是OOP:Object Oriented Programming,翻译过来就是面向对象编 ...
- 【Spring源码分析系列】bean的加载
前言 以 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过 ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- Spring IOC 容器源码分析系列文章导读 1
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- Spring源码分析(一):从哪里开始看spring源码(系列文章基于Spring5.0)
一.概述 对于大多数第一次看spring源码的人来说,都会感觉不知从哪开始看起,因为spring项目源码由多个子项目组成,如spring-beans,spring-context,spring-cor ...
- Tomcat8源码分析系列-spring boot集成tomcat
前言 本文基于 spring boot 1.5.9 spring boot 支持目前主流的 servlet 容器,包括 tomcat.jetty.undertow,可以在我们的项目中方便地集成这些 s ...
最新文章
- R语言聚类分析之基于划分的聚类KMeans实战:基于菌株数据
- onclick 获取点击之后的img 的id_前端,点击按钮跳出视频带蒙层,且视频永远居于屏幕中间...
- 一块CPU就能运行超逼真水流特效!胡渊鸣的算法被这样实现,本人看了都说好...
- C 把两个bitmap文件合并成一个bitmap文件
- BI国产化替代进入实质阶段,新产品新方案提高加速度
- ResNet网络解决的一些事
- 数塔(hdoj 2084,动态规划递推)
- 2019年的第三场LiveVideoStackCon有何不同?
- 不相交集的求并算法(按集合大小求并+按高度求并)
- 12_04_Linux软件管理之四yum
- python 替换文本 通配符_使用通配符搜索和替换文本文件中的字符串
- (转)Spring Boot 2 (三):Spring Boot 开源软件都有哪些?
- 动态规划之袋鼠过河问题
- Python3 有序字典—OrderedDict()
- 镇魂街武神躯怎么修改服务器,镇魂街武神躯怎么重置守护灵_守护灵重置方法_3DM手游...
- 【机器学习|数学基础】Mathematics for Machine Learning系列之矩阵理论(22):方阵函数在微分方程组中的应用
- 【工具】URLEncode
- word实现奇数页页眉用本章标题,偶数页用论文标题
- 区块链学习——HyperLedger-Fabric v0.6环境搭建详细过程
- android实现日历
热门文章
- python爬虫,虎牙房间爬取(selenium)
- 【前端问题分析】从输入 URL 到浏览器接收的过程中发生了什么事情?
- 服务器能ping通 不能打开网页,能ping通,但是不能打开网页
- 分享|2020年义务教育入学信息采集今日开始!手把手教您如何快速操作|方格教育
- react-emotion_如何使用Web Speech API和Node.js构建语音转Emotion Converter
- 逻辑输出光电耦合器 TLP350
- 8寸Single机台喷淋去胶加工服务
- AUTH权限通用后台管理控制系统、阿里云UI高端大气
- Python控制AutoCAD画换热器,一下解决一个班的课设绘图
- php自动调用打印机直接打印,PHP自动打印到网络打印机?