Spring IOC理论总结

目录

谈谈自己对于 Spring IoC 的了解

什么是IOC

深入理解 IoC

依赖注入

为何要用IoC

IoC 容器

BeanFactory和ApplicationContext的区别

BeanFactory 与 FactoryBean的区别?

依赖注入的方式

自动装配

Setter

构造器注入

接口回调注入

方法注入

静态工厂的方法注入

实例工厂的方法注入

类型选择

@Autowired @Qualifier @Resource总结

依赖查找和依赖注入

Spring bean

什么是 bean?

bean 的作用域有哪些?

如何配置 bean 的作用域呢?

单例 bean 的线程安全问题了解吗?

Spring创建了单例对象,如果多线程并发对属性赋值,造成相互覆盖的情况,如何处理。

@Component, @Repository, @Service的区别

@Component 和 @Bean 的区别是什么?

将一个类声明为 bean 的注解有哪些?

bean 的生命周期?

Bean初始化有哪些方式

Spring IOC的启动流程

BeanDefinitionReader

BeanFactoryPostProcesser

BeanFactory

Spring IoC实现(解耦)

介绍

IoC容器初始化

Resource定位

BeanDefinition的载入

BeanDefinition的注册

IoC容器的依赖注入

Bean的其他


注意:本文参考

docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md · SnailClimb/JavaGuide - Gitee.com

一分钟带你玩转 Spring IoC

给学妹看的SpringIOC 面试题(上)

给学妹看的SpringIOC 面试题(下)

初始化Spring Bean:Bean初始化有哪些方式?_码农架构-CSDN博客_springbean初始化方式

@Component, @Repository, @Service的区别_fansili的博客-CSDN博客_@service和@compent区别

谈谈自己对于 Spring IoC 的了解

IoC(Inverse of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。

什么是IOC

IoC - Inversion of Control, 控制反转。

控制反转就是把创建和管理 bean 的过程转移给了第三方。而这个第三方,就是 Spring IoC Container,对于 IoC 来说,最重要的就是容器

容器负责创建、配置和管理 bean,也就是它管理着 bean 的生命,控制着 bean 的依赖注入。

通俗点讲,因为项目中每次创建对象是很麻烦的,所以我们使用 Spring IoC 容器来管理这些对象,需要的时候你就直接用,不用管它是怎么来的、什么时候要销毁,只管用就好了。

举个例子,就好像父母没时间管孩子,就把小朋友交给托管所,就安心的去上班而不用管孩子了。托儿所,就是第三方容器,负责管理小朋友的吃喝玩乐;父母,相当于程序员,只管接送孩子,不用管他们吃喝。

等下,bean 又是什么?

Bean 其实就是包装了的 Object,无论是控制反转还是依赖注入,它们的主语都是 object,而 bean 就是由第三方包装好了的 object。(想一下别人送礼物给你的时候都是要包装一下的,自己造的就免了。

Bean 是 Spring 的主角,有种说法叫 Spring 就是面向 bean 的编程(Bean Oriented Programming, BOP)。

深入理解 IoC

这里用经典 class Rectangle 来举例:

两个变量:长和宽

自动生成 set() 方法和 toString() 方法

注意 :一定要生成 set() 方法,因为 Spring IoC 就是通过这个 set() 方法注入的;toString() 方法是为了我们方便打印查看。

public class Rectangle {
    private int width;
    private int length;

    public Rectangle() {
        System.out.println("Hello World!");
    }


    public void setWidth(int widTth) {
        this.width = widTth;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "width=" + width +
                ", length=" + length +
                '}';
    }
}

然后在 test 文件中手动用 set() 方法给变量赋值。

嗯,其实这个就是「解藕」的过程!

public class MyTest {
  @Test
  public void myTest() {
    Rectangle rect = new Rectangle();
    rect.setLength(2);
    rect.setWidth(3);
    System.out.println(rect);
  }
}

其实这就是 IoC 给属性赋值的实现方法,我们把「创建对象的过程」转移给了 set() 方法,而不是靠自己去 new,就不是自己创建的了。

这里我所说的“自己创建”,指的是直接在对象内部来 new,是程序主动创建对象的正向的过程;这里使用 set() 方法,是别人(test)给我的;而 IoC 是用它的容器来创建、管理这些对象的,其实也是用的这个 set() 方法,不信,你把这个这个方法去掉或者改个名字试试?

几个关键问题:

何为控制,控制的是什么?

答:是 bean 的创建、管理的权利,控制 bean 的整个生命周期。

何为反转,反转了什么?

答:把这个权利交给了 Spring 容器,而不是自己去控制,就是反转。由之前的自己主动创建对象,变成现在被动接收别人给我们的对象的过程,这就是反转。

举个生活中的例子,主动投资和被动投资。

自己炒股、选股票的人就是主动投资,主动权掌握在自己的手中;而买基金的人就是被动投资,把主动权交给了基金经理,除非你把这个基金卖了,否则具体选哪些投资产品都是基金经理决定的。

依赖注入

回到文档中,第二句话它说:IoC is also known as DI.

我们来谈谈 dependency injection - 依赖注入。

何为依赖,依赖什么?

程序运行需要依赖外部的资源,提供程序内对象的所需要的数据、资源。

何为注入,注入什么?

配置文件把资源从外部注入到内部,容器加载了外部的文件、对象、数据,然后把这些资源注入给程序内的对象,维护了程序内外对象之间的依赖关系。

所以说,控制反转是通过依赖注入实现的。但是你品,你细品,它们是有差别的,像是「从不同角度描述的同一件事」

IoC 是设计思想,DI 是具体的实现方式;

IoC 是理论,DI 是实践;

从而实现对象之间的解藕。

当然,IoC 也可以通过其他的方式来实现,而 DI 只是 Spring 的选择。

IoC 和 DI 也并非 Spring 框架提出来的,Spring 只是应用了这个设计思想和理念到自己的框架里去。

为何要用IoC

那么为什么要用 IoC 这种思想呢?换句话说,IoC 能给我们带来什么好处?

答:解藕。

它把对象之间的依赖关系转成用配置文件来管理,由 Spring IoC Container 来管理。

在项目中,底层的实现都是由很多个对象组成的,对象之间彼此合作实现项目的业务逻辑。但是,很多很多对象紧密结合在一起,一旦有一方出问题了,必然会对其他对象有所影响,所以才有了解藕的这种设计思想。

如上图所示,本来 ABCD 是互相关联在一起的,当加入第三方容器的管理之后,每个对象都和第三方法的 IoC 容器关联,彼此之间不再直接联系在一起了,没有了耦合关系,全部对象都交由容器来控制,降低了这些对象的亲密度,就叫“解藕”。

IoC 容器

既然说容器是 IoC 最重要的部分,那么 Spring 如何设计容器的呢?

使用 ApplicationContext,它是 BeanFactory 的子类,更好的补充并实现了 BeanFactory 的。

BeanFactory 简单粗暴,可以理解为 HashMap:

Key - bean name

Value - bean object

但它一般只有 get, put 两个功能,所以称之为“低级容器”。

而 ApplicationContext 多了很多功能,因为它继承了多个接口,可称之为“高级容器”。在下文的搭建项目中,我们会使用它。

ApplicationContext 的里面有两个具体的实现子类,用来读取配置配件的:

ClassPathXmlApplicationContext - 从 class path 中加载配置文件,更常用一些;

FileSystemXmlApplicationContext - 从本地文件中加载配置文件,不是很常用,如果再到 Linux 环境中,还要改路径,不是很方便。

当我们点开 ClassPathXmlApplicationContext 时,发现它并不是直接继承 ApplicationContext 的,它有很多层的依赖关系,每层的子类都是对父类的补充实现。

而再往上找,发现最上层的 class 回到了 BeanFactory,所以它非常重要。

要注意,Spring 中还有个 FactoryBean,两者并没有特别的关系,只是名字比较接近,所以不要弄混了顺序。

为了好理解 IoC,我们先来回顾一下不用 IoC 时写代码的过程。

BeanFactory和ApplicationContext的区别

BeanFactory:

是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;

ApplicationContext:

应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;

1) 国际化(MessageSource)

2) 访问资源,如URL和文件(ResourceLoader)

3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层  

4) 消息发送、响应机制(ApplicationEventPublisher)

5) AOP(拦截器)


两者装载bean的区别

BeanFactory:

BeanFactory在启动的时候不会去实例化Bean,从容器中拿Bean的时候才会去实例化;

ApplicationContext:

ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化; 

我们该用BeanFactory还是ApplicationContent

延迟实例化的优点:(BeanFactory)

应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势; 

不延迟实例化的优点: (ApplicationContext)

1. 所有的Bean在启动的时候都加载,系统运行的速度快; 

2. 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题 

3. 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成) 

BeanFactory 与 FactoryBean的区别?

BeanFactory 是 IoC 底层容器 ,FactoryBean 是 创建 Bean 的一种方式,帮助实现复杂的初始化逻辑

依赖注入的方式

依赖注入只是把bean添加到IOC容器的一种方式。

从依赖注入的方式来说整体可以分为两大类来处理,一种是手动方式,一种是自动方式。

手动方式:

1 XML 资源配置元信息(比较常见)

2 Java 注解配置元信息 (比较常见)

3 API 配置元信息(不太常用)

自动方式:

1 Autowiring

依赖注入的方式有上面的两种,但是也可按注入的类型来区分:

1 Setter注入

2 构造器注入

3 接口注入

4 方法注入

5 静态工厂

6 实例工厂

自动装配

聊到依赖注入那么首先需要先聊聊 Autowiring Modes自动绑定模式

Spring的官方文档中对Autowiring Modes解释是:

Spring 容器可以自动装配协作 bean 之间的关系。通过检查 ApplicationContext 的内容,您可以让 Spring 自动为您的 bean 解析协作者(其他 bean)

同时也提出了4种自动装配模式

no:(默认)无自动装配。Bean 引用必须由ref元素定义。对于大型部署,建议不要更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。

byName:按属性名称自动查找。Spring 寻找与需要自动装配的属性同名的 bean。例如,如果一个 bean 定义被设置为按名称自动装配,并且包含一个master属性(即,它具有setMaster(..)方法),那么 Spring 将查找一个名为master的 bean 定义并使用它来设置属性。

byType:如果容器中恰好存在一个该属性类型的 bean,则使该属性自动装配。如果存在多个错误,则会引发致命异常,这表明您可能不对该 bean 使用byType自动装配。如果没有匹配的 bean,则什么也不会发生(未设置该属性)。

constructor:类似于byType,但适用于构造函数参数。如果容器中不存在构造函数参数类型的一个 bean,则将引发致命错误。

虽然官方文档提出了Autowiring自动绑定方式,但是在我们的真实的业务场景中,相对来说是用的比较少的,因为它有一定的局限性,而且Spring官方文档中也列出了其中的不足点。

自动装配的局限性和缺点(官方文档链接)

propertyconstructor-arg设置中的显式依赖项始终会覆盖自动装配。您不能自动连接简单属性,例如基元,StringsClasses(以及此类简单属性的数组)。此限制是设计使然  PS:针对这种情况可以通过另外的一种方式@value等进行转化来处理这个场景。

自动装配不如显式接线精确。尽管如前所述,Spring 还是小心避免在可能产生意外结果的模棱两可的情况下进行猜测。SpringManagement 的对象之间的关系不再明确记录。

容器内的多个 bean 定义可能与要自动装配的 setter 方法或构造函数参数指定的类型匹配。对于数组,集合或Map实例,这不一定是问题。但是,对于需要单个值的依赖项,不会任意解决此歧义。如果没有唯一的 bean 定义可用,则引发异常。

说完这么多文档的基础知识,那么接下来就是开始demo测试环节,来加深理解一下上面的说的那么多到底是个啥。

Setter

先从注入的类型先分析怎么样的一种方式叫Setter方式注入

/构建一个测试Service
public class SetterServiceInjection {
    public void testMethod(String param) {
        System.out.println(param);
    }
}

public class SetterServiceInjectionTest {
    private SetterServiceInjection setterServiceInjection;

    // Setter方式注入
    public void setSetterServiceInjection(SetterServiceInjection setterServiceInjection) {
        this.setterServiceInjection = setterServiceInjection;
    }

    public void testMethod(){
        setterServiceInjection.testMethod("Setter方式注入");
    }

  
  // 测试启动demo
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("class
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值