本系列以官方开发文档+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对象注入到代码中)

(这里依赖注入的装配采用的是自动装配,自动装配不等于依赖注入)

我们先来看对象注册到容器再到被注入的过程:

  1. 声明相关的类/对象信息及依赖关系,然后将对象和对应的依赖关系等注册到容器中(通过xml、注解、java cofiguration方式)
  2. 容器拿到依赖关系后会创建、维护具有对应依赖关系的bean
  3. 当程序需要的时候,此时可以用从容器中获取得到对应的bean(容器注入给我们对应的对象)

而依赖注入就相当于容器将我们所需要的资源动态注入给b对象。

(其中IOC就是一种思想,不用在自己每次要用对象时去手动new一个对象,将对像的控制权交给容器,而在你需要时候容器会将对应的bean注入给你)下面就图中的三个步骤作分析:

spring编程的风格

  1. schemal-based-------xml
  2. annotation-based-----annotation 注解
  3. 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,而还有各个对象的依赖关系还没有给到容器,而自动装配则帮我们少了依赖关系的第二次配置)

那么自动装配有几种方式?

  1. no:(默认值),不采用自动装配。
  2. byName:容器将查找与属性名相同的Bean,然后自动注入到该属性中,如果没有找到,则该属性将不会被注入。当然,使用自动装配时,也可以显式地注入Bean 的部分属性。
  3. byType:注入过程与byName 类似, 不同的是,容器查找的是与属性的设值方法参数类型兼容的Bean。
  4. constructor:容器将试图找出所有与构造方法的参数类型兼容的Bean ,然后确定某个合适的构造方法。如果没有符合调用任何构造方法所需的Bean,则容器将抛出UnsatisfiedDependencyException。
  5. 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源码分析系列(一)相关推荐

  1. Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean

    前言 spring创建bean的方式 测试代码准备 createBeanInstance()方法分析 instantiateUsingFactoryMethod()方法分析 总结 spring创建be ...

  2. Spring源码分析系列-Bean的生命周期(总结篇)

    ApplicationContext和BeanFactory   BeanFactory是Spring中的顶层接口,只负责管理bean,而ApplicationContext也实现了BeanFacto ...

  3. Spring源码分析系列-循环依赖和三级缓存

    目录 循环依赖 多级缓存 一级缓存 二级缓存 当循环依赖遇上AOP 三级缓存 Spring三级缓存源码实现 总结 循环依赖   BeanFactory作为bean工厂管理我们的单例bean,那么肯定需 ...

  4. spring源码分析系列(二)AOP应用

    这里讲AOP应用 先来说说一个比较虚无缥缈的问题:什么是AOP? 1.OOP 在说AOP之前,我们需要先看看什么是OOP:Object Oriented Programming,翻译过来就是面向对象编 ...

  5. 【Spring源码分析系列】bean的加载

    前言 以 BeanFactory bf  = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过 ...

  6. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  7. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  8. Spring源码分析(一):从哪里开始看spring源码(系列文章基于Spring5.0)

    一.概述 对于大多数第一次看spring源码的人来说,都会感觉不知从哪开始看起,因为spring项目源码由多个子项目组成,如spring-beans,spring-context,spring-cor ...

  9. Tomcat8源码分析系列-spring boot集成tomcat

    前言 本文基于 spring boot 1.5.9 spring boot 支持目前主流的 servlet 容器,包括 tomcat.jetty.undertow,可以在我们的项目中方便地集成这些 s ...

最新文章

  1. R语言聚类分析之基于划分的聚类KMeans实战:基于菌株数据
  2. onclick 获取点击之后的img 的id_前端,点击按钮跳出视频带蒙层,且视频永远居于屏幕中间...
  3. 一块CPU就能运行超逼真水流特效!胡渊鸣的算法被这样实现,本人看了都说好...
  4. C 把两个bitmap文件合并成一个bitmap文件
  5. BI国产化替代进入实质阶段,新产品新方案提高加速度
  6. ResNet网络解决的一些事
  7. 数塔(hdoj 2084,动态规划递推)
  8. 2019年的第三场LiveVideoStackCon有何不同?
  9. 不相交集的求并算法(按集合大小求并+按高度求并)
  10. 12_04_Linux软件管理之四yum
  11. python 替换文本 通配符_使用通配符搜索和替换文本文件中的字符串
  12. (转)Spring Boot 2 (三):Spring Boot 开源软件都有哪些?
  13. 动态规划之袋鼠过河问题
  14. Python3 有序字典—OrderedDict()
  15. 镇魂街武神躯怎么修改服务器,镇魂街武神躯怎么重置守护灵_守护灵重置方法_3DM手游...
  16. 【机器学习|数学基础】Mathematics for Machine Learning系列之矩阵理论(22):方阵函数在微分方程组中的应用
  17. 【工具】URLEncode
  18. word实现奇数页页眉用本章标题,偶数页用论文标题
  19. 区块链学习——HyperLedger-Fabric v0.6环境搭建详细过程
  20. android实现日历

热门文章

  1. python爬虫,虎牙房间爬取(selenium)
  2. 【前端问题分析】从输入 URL 到浏览器接收的过程中发生了什么事情?
  3. 服务器能ping通 不能打开网页,能ping通,但是不能打开网页
  4. 分享|2020年义务教育入学信息采集今日开始!手把手教您如何快速操作|方格教育
  5. react-emotion_如何使用Web Speech API和Node.js构建语音转Emotion Converter
  6. 逻辑输出光电耦合器   TLP350
  7. 8寸Single机台喷淋去胶加工服务
  8. AUTH权限通用后台管理控制系统、阿里云UI高端大气
  9. Python控制AutoCAD画换热器,一下解决一个班的课设绘图
  10. php自动调用打印机直接打印,PHP自动打印到网络打印机?