Spring IoC容器与Bean管理(一)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: Spring IoC容器与Bean管理

从本节开始,我将要学习作为java高级阶段最最最最重要的一个框架体系,名为Spring。Spring是整个Java生态中最重要的一环。因为我也是初学,所以我的概括也不一定全面和精炼。写这一章只是为自己以后复习。


一.Spring IoC容器与Bean管理


这一小节,要学习Spring入门、Spring XML的配置、对象实例化配置、依赖注入配置、注解与Java Config、Spring实现单元测试。


1.Spring快速入门


IoC控制反转


◈ IoC控制反转,全称为Inversion of Control,是一种设计理念。


◈ 它并不是一种技术,而是一种宏观的设计理念,是现代程序设计遵循的标准,由代理人来创建与管理对象,调用者通过代理人来获取对象。IoC的目的是用来降低对象(或计算机代码)之间的直接耦合。


◈ 加入IoC容器将对象统一管理,让对象关联变为弱耦合。


下面举一个生活中的案例:

我非常喜欢吃苹果,但是摆在我面前有一件非常苦恼的事情,市场上各种各样的苹果琳琅满目,苹果有很多品种,口感和价格。假如现在在我面前放有各种各样的苹果,但我并不知道他们是什么品种,多少钱。我平时喜欢吃甜的而且脆的苹果,那么问题来了,如果不在他人介入的情况下,我想得到摆在我面前的几种苹果中最符合我口味的,该怎么做呢?是不是我就得翻页大量的资料去了解每一种水果的类型和场地或者把他们全部品尝一遍啊?这是很麻烦的一件事情。这个控制权是由我这个客户主动发起的,我必须要掌握所有对象的细节特征以后,才可以做出正确的选择。如果摆在我面前的是100种呢?除非我是专业人士,否则肯定蒙圈。


d6c877517034437a8b42db6e1db79a94.png


但幸运的是,有的聪明人就看到了其中的商机,随着市场经济的不断发展,各大水果超市和水果店孕育而生。水果摊是有老板的,我们去买水果的时候,我们并不需要知道这些苹果具体的特征。只需要找到水果摊的老板,和他说”哪个苹果是脆甜的啊?“ 然后老板就会告诉我”这种这种和这种苹果是脆甜的,他们的甜度各有不同,口感也各有不同。“ 由于水果摊老板常年从事这个行业,他当然比我要了解得多。我只需要听从他的意见进行采购就可以了。正是由于有水果摊老板的存在,我就把我获取对象的权力交给了水果摊老板。是由他替我做出了相应的决定。水果摊老板使我们日常生活得到了极大的便利。消费者和产品对象通过代理人进行了解耦,这种解耦带来的直接的好处就是对象之间可以灵活的变化,假如水果店老板发现市场上有了一种更脆甜的苹果,我们再去向老板购买苹果时,它就会向我推荐这种更好的苹果了,我完全不用关心它的其他特性,因为这是老板的事情,我唯一的目的就是获取到最好吃的脆甜的苹果,这就是IoC的控制反转的目的。诸如这样的例子其实还有很多。比如你来到一个全新的城市需要安居,这时要找的是中介,而不是随便地去乱找房东。通过中介的信息来选择在哪住,解决了找房子的各种各样的麻烦。这些都是软件工程里面IoC控制反转的核心体现。

·

DI依赖注入


IoC是一种宏观的设计理念,与编程语言无关,是现代程序设计遵循的标准。


DI(Dependency Injection)是具体技术实现,是微观实现。编程环境下到底使用哪些编程技术来完成在程序运行过程中对象的创建与绑定的工作。DI在不同语言中使用到的具体技术是不一样的。


DI在Java中利用反射技术实现对象注入(Injection)


Spring概述


Spring的含义:Spring可以从狭义和广义两个角度看待。


狭义的Spring是指Spring框架(Spring Framework) ,广义的Spring是指Spring生态体系。


狭义的Spring框架:

Spring框架是企业开发复杂性的一站式解决方案。也就是说,通过Spring框架,帮我们把以前在实际开发中那些开发体验不太友好,功能缺失的部分通过Spring的补充让整个体系更加的完整。Spring框架的核心是IoC容器与AOP面向切面编程。 IoC容器是所有对象管理的基础,AOP是建立在IoC容器的基础上才得到的。Spring IoC负责创建与管理系统对象,并在此基础上拓展功能。


广义的Spring生态体系:

Spring IoC是整个Spring生态体系中最核心,也是最基础的一部分。这是因为系统中所有的对象被统筹管理,在此基础上生根发芽,拓展出来了若干不同功能的子项目。可以说,目前的spring是一站式的综合解决方案,无论要做web应用开发,还是做安卓开发,或者是分布式应用,都是可以的。


c62491fc5a204bd8afe246dabf6c5d4b.png


以上就是Spring能做到的事情,在这些事情的基础上派生出来了几十种不同的项目,有Spring Boot 、Spring Framework、spring data 、spring cloud 、spring cloud data flow、spring security 、spring session、SpringGraphQL等等等,具体参考https://spring.io/projects


现阶段,我要学习的是Spring Framework这一部分。


传统的开发方式:对象直接引用导致对象硬性关联,程序难以拓展维护。

假如有一个使用者,要使用A这个对象,但是A对象实现某个功能,要额外的去new一个B对象。可以把A对象看成是一个service服务,B对象是一个Dao。这个过程是我们以前标准的开发方式。但是它也有一个巨大的弊端,当这个Dao随着我们开发的过程不断地演化,已经不适合了,由其他程序员开发了一个C对象,这是如果A要抛弃B,选择C,就要修改源代码,去new C对象才可以。这就意味着我们的程序就需要经过编译以后重新上线,才能够生效。就会涉及重新测试,重新发版,重新审批等等等,这一套流程是非常繁琐的。所以在我们的项目实践的过程中,并不推荐由具体的使用者来去new创建对应的对象。


a1a702e9c33a4669a675d2e630619ca2.png


那就要用到Spring IoC 容器了,采用被动的形式,来创建和管理这些对象,而我们使用者只是单纯的将容器中的对象进行提取就可以了。IoC是Spring生态的低基,用于统一创建与管理对象依赖。


6288773bd6b44552a42688e0d104b8d0.png


就拿刚才的例子,如果换成Spring来做的话,首先,作为Spring,它会提供一个Spring IoC容器,这个容器其实是一个抽象的东西,相当于在我们Java的运行的内存中开辟了一段空间,这个空间是由Spring进行管理的。所有的对象都不是由我们使用者或A对象来创建,而都是由Spring IoC容器统一负责创建。创建好了以后,A对象是依赖于B对象的,当然也不会通过A对象去new B对象,而是通过反射技术,将A对象的依赖 B对象 注入到A对象中。作为使用者来说,我并不需要关注在容器内部到底有几个对象,对象之间的关系是什么样的,我只要关注在什么地方将我需要的对象提取出来就行了。也就是说不再面向具体的对象,而是面向容器,通过容器获取到需要的对象。


再拿那个水果摊的例子来比喻一下,spring IoC就是水果的仓库,spring框架就是水果摊老板,使用者就是顾客,顾客告诉水果摊老板给我来红的大的天的水果,老板就将对应符合要求的水果提取出来。


Spring IoC容器职责:

将对象的控制权交给第三方控制管理(IoC控制反转),利用java反射技术实现运行时对象创建与关联(DI依赖注入),基于配置提高应用程序的可维护性与可拓展性。


Spring IoC初体验


案例如下:


89e7cc21e3624a4cb73079a9d1d2d393.png


针对于孩子们的口味不同,我们如何让孩子直接得到自己喜欢的苹果呢?


下面通过传统的程序来演示一下:

打开Idea,新建一个空的Maven项目,项目名为s01

在entity里面创建两个java实体类,分别是苹果类Apple和孩子类Child,代码如下:


Apple.java


package com.haiexijun.ioc.entity;
public class Apple {
    private String title;
    private String color;
    private String origin;
    public Apple() {
    }
    public Apple(String title, String color, String origin) {
        this.title = title;
        this.color = color;
        this.origin = origin;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getOrigin() {
        return origin;
    }
    public void setOrigin(String origin) {
        this.origin = origin;
    }
}


Child.java


package com.haiexijun.ioc.entity;
public class Child {
    private String name;
    private Apple apple;
    public Child() {
    }
    public Child(String name, Apple apple) {
        this.name = name;
        this.apple = apple;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Apple getApple() {
        return apple;
    }
    public void setApple(Apple apple) {
        this.apple = apple;
    }
    public void eat(){
        System.out.println(name+"吃到了"+apple.getOrigin()+"种植的"+apple.getTitle());
    }
}
```java
然后创建一个Application来编写逻辑代码:
package com.haiexijun.ioc;
import com.haiexijun.ioc.entity.Apple;
import com.haiexijun.ioc.entity.Child;
public class Application {
    public static void main(String[] args) {
        //创建3个苹果
        Apple apple1=new Apple("红富士","红色","欧洲");
        Apple apple2=new Apple("青苹果","绿色","中亚");
        Apple apple3=new Apple("金帅","黄色","中国");
        //创建孩子,完成了苹果与孩子两个对象的关联
        Child lily=new Child("莉莉",apple1);
        Child andy=new Child("安迪",apple2);
        Child luna=new Child("露娜",apple3);
        //孩子吃苹果
        lily.eat();
        andy.eat();
        luna.eat();
    }
}


运行后可以得到我们需要的结果。


c3aca9539d3e4cbb949fc3f9e5ad6f9a.png


但是,上面的这些代码在实际工作中会有很多的弊端,下面依依来分析:

首先,苹果的这些描述,都写死在程序代码中了,而我们知道,苹果的这些属性随着季节和时间会相应的变化。一旦属性发生变化后,我们就必须去修改程序的源代码,就要对应用程序进行重新发布和重新上线。而且我程序里写了3个对象,那么它就只会创建3个对象。如果新加入一个孩子,就要修改源代码,程序的可维护性和可扩展性非常不足。而且是硬关联,通过构造方法参数来创建对象进行设置,者意味着我们程序运行以后,这个孩子和苹果的关系就已经是确定了,这个确定关系是在程序编译时就完成的,这是一件非常死板的事情。举个例子,现在露娜长大了,不再喜欢此软软的金帅了,他也开始喜欢吃红富士,而且莉莉也想尝尝金帅的味道,如果按上面传统的写法,就得去修改源代码进行调整。


下一小节就用Spring IoC来实现一下这个案例。


使用XML方式实现Spring IoC


下面就将上面的代码修改为Spring IoC管理的程序,体验一下Spring IoC的强大。

在这之前,我必须要强调一下,因为这是我们第一次接触spring,这个案例的目的是为了让我们对Spring有一个感性的认知,针对于里面的各个配置以及属性,会在后面的小节里详细进行学习。


1.配置Maven依赖

spring-context是spring IoC容器最小的依赖范围,只有引入了spring-context,我们才可以对程序进行管理。


<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.13</version>
</dependency>

引入依赖后,可以点击External Libraries看看导入了那些依赖:


0f24d85f769c41bda12d16b04cad2597.png


2.在resources目录下创建一个xml文件,applicationContext.xml

applicationContext.xml这个文件是spring IoC的核心配置文件,所有对象的创建和关联的设置,都是在这个xml来进行的。

那么如何对这个xml进行配置呢?我们先打开spring官网,然后进入到里面的Project里的Spring Framework的帮助文档的Core部分。然后寻找到1.2.1的xml配置,复制其schema约束到配置文件中。

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-metadata


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>


然后idea也非常智能,会马上显示是否创建context。


061b88697f31433e99f5cea98aa1f678.png


我们点击创建,然后点击OK确定


4b207ee75f5c4060a20353d46c2edcb2.png


书写bean


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--在IoC容器启动时,自动由Spring实例化Apple,取名为sweetApple放入到容器中-->
    <bean id="sweetApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="红富士"></property>
        <property name="origin" value="欧洲"></property>
        <property name="color" value="红色"></property>
    </bean>
    <bean id="sourApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="青苹果"></property>
        <property name="origin" value="中亚"></property>
        <property name="color" value="绿色"></property>
    </bean>
    <bean id="softApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="金帅"></property>
        <property name="origin" value="中国"></property>
        <property name="color" value="黄色"></property>
    </bean>
</beans>


3.创建一个有main方法的java类,编写如下代码:


package com.haiexijun.ioc;
import com.haiexijun.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
    public static void main(String[] args) {
        //加载指定的xml文件,来初始化IoC容器,context本身就指代了spring IoC容器
        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Apple sweetApple = context.getBean("sweetApple", Apple.class);
        System.out.println(sweetApple.getTitle());
    }
}


运行后打印红富士。


我们对比就会发现一个非常大的好处,就是把原本的代码变成了可配置的文本,在xml文本中更改配置,不用动任何一行的源代码,就能对内容进行调整。


下面继续来写代码,演示一下对象关联。


4.在applicationContext.xml中增加3个孩子对象的bean


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--在IoC容器启动时,自动由Spring实例化Apple,取名为sweetApple放入到容器中-->
    <bean id="sweetApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="红富士"/>
        <property name="origin" value="欧洲"/>
        <property name="color" value="红色"/>
    </bean>
    <bean id="sourApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="青苹果"/>
        <property name="origin" value="中亚"/>
        <property name="color" value="绿色"/>
    </bean>
    <bean id="softApple" class="com.haiexijun.ioc.entity.Apple">
        <property name="title" value="金帅"/>
        <property name="origin" value="中国"/>
        <property name="color" value="黄色"/>
    </bean>
    <bean id="lily" class="com.haiexijun.ioc.entity.Child">
        <property name="name" value="莉莉"/>
        <property name="apple" ref="sweetApple"/>
    </bean>
    <bean id="andy" class="com.haiexijun.ioc.entity.Child">
        <property name="name" value="安迪"/>
        <property name="apple" ref="sourApple"/>
    </bean>
    <bean id="luna" class="com.haiexijun.ioc.entity.Child">
        <property name="name" value="露娜"/>
        <property name="apple" ref="softApple"/>
    </bean>
</beans>


里面的Child通过ref属性来关联苹果。

下面编写测试代码


package com.haiexijun.ioc;
import com.haiexijun.ioc.entity.Apple;
import com.haiexijun.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
    public static void main(String[] args) {
        //加载指定的xml文件,来初始化IoC容器,context本身就指代了spring IoC容器
        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Apple sweetApple = context.getBean("sweetApple", Apple.class);
        System.out.println(sweetApple.getTitle());
        //从IoC容器中提取beanId=lily的对象
        Child lily=context.getBean("lily",Child.class);
        lily.eat();
        Child andy=context.getBean("andy",Child.class);
        andy.eat();
        Child luna=context.getBean("andy",Child.class);
        luna.eat();
    }
}

运行后,都吃到了水果。

可能这里你并没有完全发现IoC的好处。但是通过xml的配置,在以后的项目中确实可以提高程序的可维护性和可拓展性。

到这里,对spring IoC的算是有了基本的认识。下面会对里面的各项配置进行详细的讲解。

相关文章
|
18天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
72 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
87 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
45 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
88 1
|
1月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
71 0
|
2月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
6月前
|
XML Java 数据格式
Spring IoC容器初始化过程(xml形式)
Spring IoC容器初始化过程(xml形式)
81 0
下一篇
无影云桌面