《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 2

《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 2

2.2 IOC 的两种实现方式

了解 IOC 思想的由来之后,下面学习 Spring Framework 的 IOC 思想的具体实现。IOC 的实现方式包含两种,分别是依赖查找 (Dependency Lookup) 与依赖注入 (Dependency Injection)。

2.2.1 依赖查找

依赖查找的含义是,根据指定的对象名称或对象的所属类型,主动从 IOC 容器中获取对应的具体对象(后续将这种从 IOC 容器中获取的对象称为 bean 对象)​。

1.根据名称查找 (byName)

(1)创建工程,引入依赖

(2)编写配置文件

Spring Framework 使用 XML 配置文件的方式来描述类和对象的定义信息。

xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

(3)声明普通类并注册

可以创建一个普通的 Person 类,其内部不需要编写任何代码。随后在 quickstart-byname.xml 文件中使用 Spring Framework 的定义规则,将Person 声明到配置文件中。配置文件的编写方式非常简单,使用 标签来声明一个注册到 IOC 容器中的 Bean,它的 id 为 "person"​,对应的类型就是上面创建的 Person 类。

java 复制代码
package com.yangjunbo.dl.examplea;

/**
 * ClassName: Person
 * Package: com.yangjunbo.dl.examplea
 * Description:
 *
 * @Author 杨钧博
 * @Create 2026/8/11 16:01
 * @Version 1.0
 */
public class Person {
}
java 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.yangjunbo.dl.examplea.Person"></bean>

</beans>

(4)创建启动类并测试

有了配置文件后下一步就是要读取和解析配置文件,我们创建一个 QuickstartByNameApplication 类,并声明 main 方法编写启动逻辑。读取quickstart-byname.xml 文件的方法有很多,因为是第一个示例,所以我们选择一种相对简单的方法。

简单解释代码的含义。要想读取 quickstart-byname.xml 配置文件,需要一个载体来加载,示例代码中选择使用ClassPathXmlApplicationContext 来加载,可以从当前项目的类路径 (classpath) 下,找到 dl 目录下的 quickstart-byname.xml 文件。加载完成后,我们使用 BeanFactory 接口来接收该对象(多态的思想)​。之后从 BeanFactory 中调用 getBean 方法,从 IOC 容器中取出名为 "person" 的对象,并强制转换为 Person 类型,最后打印即可。

java 复制代码
package com.yangjunbo.dl.examplea;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuickstartByNameApplication {
    
    public static void main(String[] args) throws Exception {
        BeanFactory factory = new ClassPathXmlApplicationContext("dl/quickstart-byname.xml");
        Person person = (Person) factory.getBean("person");
        System.out.println(person);
    }
}

运行 QuickstartByNameApplication 的 main 方法,控制台可以成功打印出 Person 的全限定类名+内存地址,证明测试方法编写成功。

2.根据类型查找 (byType)

从上面的示例中可以发现一个问题:通过名称获取的 bean 对象类型只能是 Object,如果需要精准获取 IOC 容器中某一个类型的 bean 对象,则需要根据类型查找。为了演示基于类型查找,这次使用 标签声明时,我们不再指定 id 属性,相应地,启动类QuickstartByTypeApplication 中调用 getBean 方法时不再传入字符串变量,而是直接传入希望获取的 Bean 的 class 类型,而且接收对象不再需要强制类型转换。

xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yangjunbo.dl.examplea.Person"></bean>

</beans>
java 复制代码
package com.yangjunbo.dl.examplea;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuickstartByTypeApplication {
    
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("dl/quickstart-bytype.xml");
        Person person = factory.getBean(Person.class);
        System.out.println(person);
    }
}

3.接口与实现类

基于类型查找时,还可以根据接口获取对应的实现类(当然这要求实现类已经注册到 IOC 容器中)​。将 2.1 节中的 DemoDao 与 DemoDaoImpl复制到 examplea 目录中,并在 quickstart-bytype.xml 文件中加入 DemoDaoImpl 的声明定义。随后在启动类 QuickstartByTypeApplication 中使用 BeanFactory 取出 DemoDao,并打印 findAll 方法的返回数据。运行 main 方法,控制台可以打印出 aaa, bbb, ccc​,证明 DemoDaoImpl 也成功注入,并且 BeanFactory 可以根据接口类型找到对应的实现类。

java 复制代码
package com.yangjunbo.dl.examplea;

import java.util.List;

/**
 * ClassName: DeamDao
 * Package: com.yangjunbo.dao
 * Description:
 *
 * @Author 杨钧博
 * @Create 2026/8/11 11:53
 * @Version 1.0
 */
public interface DemoDao {
    List<String> findAll();
}
java 复制代码
package com.yangjunbo.dl.examplea.impl;


import com.yangjunbo.dl.examplea.DemoDao;

import java.util.Arrays;
import java.util.List;

/**
 * ClassName: DemoDaoImpl
 * Package: com.yangjunbo.dao.impl
 * Description:
 *
 * @Author 杨钧博
 * @Create 2026/8/11 11:57
 * @Version 1.0
 */
public class DemoDaoImpl implements DemoDao {
    @Override
    public List<String> findAll() {
        return Arrays.asList("aaa", "bbb", "ccc");
    }
}
xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yangjunbo.dl.examplea.Person"></bean>

    <bean class="com.yangjunbo.dl.examplea.impl.DemoDaoImpl"/>

</beans>
java 复制代码
package com.yangjunbo.dl.examplea;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuickstartByTypeApplication {
    
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("dl/examplea/quickstart-bytype.xml");
        Person person = factory.getBean(Person.class);
        System.out.println(person);

        DemoDao demoDao = factory.getBean(DemoDao.class);
        System.out.println(demoDao.findAll());
    }
}

2.2.2 依赖注入

由上面的三个示例中可以发现一个问题:以上创建的 bean 对象都是不带属性值的!如果我们要创建的 bean 对象需要一些预设的属性,那么就要涉及 IOC 的另一种实现,依赖注入。

1.简单属性值注入

为了与依赖查找的对象进行区分,我们新创建一个 Person 类,并声明两个属性 name 和 age。随后编写 XML 配置文件 inject-set.xml,将Person 类注册到 IOC 容器中。

java 复制代码
package com.yangjunbo.di.examplea;

public class Person {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.yangjunbo.di.examplea.Person"></bean>

</beans>

如果仅使用上述的代码驱动 IOC 容器初始化,那么从 IOC 容器中取出的 Person 对象中 name 与 age 的属性将全部为 null。

java 复制代码
package com.yangjunbo.di.examplea;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuickstartInjectBySetXmlApplication {
    
    public static void main(String[] args) throws Exception {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("di/examplea/inject-set.xml");
        Person person = beanFactory.getBean(Person.class);
        System.out.println(person);
    }
}

如果需要给 bean 对象的属性赋值,使用的标签是 ,这个标签有两个属性可以供我们使用,分别是 name(属性名)和 value(属性值)​。

xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.yangjunbo.di.examplea.Person">
        <property name="name" value="test-person-byset"/>
        <property name="age" value="18"/>
    </bean>

</beans>

运行 QuickstartInjectBySetXmlApplication 类的 main 方法,控制台可以打印 Person 对象的属性值,说明依赖注入属性成功。

2.关联 bean 对象注入

创建一个新的小猫类 Cat,并声明 name 和 master 属性,分别指代小猫的名字和主人。随后在 inject-set.xml 配置文件中定义一个新的 Bean,id 为 "cat"​,并使用 标签的另一个属性 ref 引用 IOC 容器中的一个现有的 bean 对象,即 person。编写完成后,在QuickstartInjectBySetXmlApplication 类中获取 Cat 对象并打印,可以发现 Cat 对象中的 master 属性就是上面的 Person 对象,关联 bean 对象的依赖注入也实现完成

java 复制代码
package com.yangjunbo.di.examplea;

public class Cat {
    private String name;
    private Person master;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person getMaster() {
        return master;
    }

    public void setMaster(Person master) {
        this.master = master;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", master=" + master +
                '}';
    }
}
xml 复制代码
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.yangjunbo.di.examplea.Person">
        <property name="name" value="test-person-byset"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="cat" class="com.yangjunbo.di.examplea.Cat">
        <property name="name" value="test-cat"/>
        <!-- ref引用上面的person对象 -->
        <property name="master" ref="person"/>
    </bean>

</beans>
java 复制代码
package com.yangjunbo.di.examplea;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuickstartInjectBySetXmlApplication {
    
    public static void main(String[] args) throws Exception {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("di/examplea/inject-set.xml");
        Person person = beanFactory.getBean(Person.class);
        System.out.println(person);

        Cat cat = beanFactory.getBean(Cat.class);
        System.out.println(cat);
    }
}

2.2.3 依赖查找与依赖注入的对比

  • 依赖查找是从 Spring 的 IOC 容器中根据 Bean 的名称或类型找到对应的 Bean。
  • 依赖注入是 Spring 的 IOC 容器在创建 Bean 的时候对 Bean 中的属性进行赋值。

2.3 BeanFactory 与 ApplicationContext

2.3.1 理解 IOC 容器

顾名思义,IOC 容器即实现了 IOC 思想的容器。容器可以理解为"一个具备创建组件并管理组件的区域"​。我们将需要创建的组件的信息,以配置文件的方式提供给 Spring Framework,在创建 IOC 容器的时候它就会自动帮我们创建这些组件,这种从加载和解析配置文件到完成容器和内部组件 (Bean) 的过程,就是 IOC 容器的基本处理流程。

2.3.2 对比 BeanFactory 与 ApplicationContext

BeanFactory 接口提供了一个对象的管理机制,ApplicationContext 是 BeanFactory 的子接口,它简化了与 AOP 的整合、消息机制、事件机制,以及对 Web 环境的扩展(WebApplicationContext 等)。

开发者要使用 ApplicationContext 而不是 BeanFactory。

2.3.3 理解 Context 与 ApplicationContext

Context 意为"上下文"​,简单地说,Context 指的是当程序运行到指定代码时,可以获取的额外数据和信息。在一篇文章中看到某个故事情节时,可能想要分析情节的作用,这就需要结合当前阅读的部分及其前后章节来综合分析,这里的"前后章节"即上下文,也就是 Context。

在软件开发中 Context 通常可以理解为我们编写的代码执行时可以获取的额外信息,包括全局变量、类中的静态成员、ThreadLocal 中的值、配置属性等。ApplicationContext 是运行 Spring Framework 应用时的上下文,可以借助 ApplicationContext 拿到 IOC 容器中的其他 Bean,无论我们在代码中是否获取和使用,这些 Bean 都是客观存在的。

相关推荐
kisbad7 小时前
Day 036|OpenAI Agents SDK 快速开始:今天跑通第一个 Agent
java·前端·javascript
憧憬成为java架构高手的小白7 小时前
langchain4j框架学习
java
0x537 小时前
Java网络编程(四)
java·网络
ly76897 小时前
Java 设计模式详解:从原则到 23 种经典模式
java·开发语言·设计模式
萧瑟余晖7 小时前
Java深入解析篇三十之Java日志框架
java·开发语言
demodeom8 小时前
Win11 + RTX 5080 本地 Coding 模型测试笔记(已弃坑)
笔记
Suxing98 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法
减瓦8 小时前
别让 Java 版本选择变成生产事故
java
ikun_文8 小时前
Java集合框架Collection拓展进阶
java·java ee
云烟成雨TD8 小时前
Micrometer 系列【63】统一观测:基于 Spring Boot 的生产级演示案例 | 基于 OTLP 集成 Prometheus + Jaeger
spring boot·云原生·prometheus