Spring IOC 容器和依赖注入(DI)

1、什么是 IOC?

IOC(Inversion of Control)控制反转,IOC的核心是将对象的创建和依赖关系的组装控制权从程序内部反转到外部容器。容器管理的是Bean的生命周期和依赖关系,而“对象之间的调用过程”通常是由业务逻辑本身决定的,容器并不管理“调用过程”

我们使用IOC的原因就是为了降低类之间的耦合度

1.1 Spring 实现 IOC 思想

  • Spring 提供了一个容器,称为IOC容器,用来充当IOC思想中的外部
  • IOC 容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在 IOC 容器中称为Bean

1.2 为什么要有 IOC?

目的:充分解耦

  • 使用 IOC 容器管理 bean(IOC)
  • 在 IOC 容器内将有依赖关系的 bean 进行关系绑定(DI)

最终效果:

  • 使用对象时不仅可以直接从 IOC 容器中获取,并且获取到的 bean 已经绑定了所有的依赖关系

2、IOC 底层原理

最主要的目的就是降低代码的耦合度,尽量地降到最低限度

  • xml 解析
  • 工厂模式
  • 反射

2.1 工厂模式和原始模式

原始模式

弊端:代码之间的耦合度高,UserService直接依赖于UserDao的具体实现,每一次调用UserService.execute()都要创建一次UserDao对象,这样会非常浪费jvm堆内存资源,

class UserService {
    execute() {
        UserDao userDao = new UserDao();
        userDao.add();
    }
}
class UserDao {
    add() {
        // CODE...
    }
}

2.1.1 工厂模式

相比于原始模式的优点:代码耦合度降低,在启动项目的时候,UserFactory会先创建一个UserDao对象存放到堆中,每次UserService需要调用UserDao.add()的时候,只需要在堆直接调用即可,可以集中管理对象的生命周期,避免重复创建对象

class UserService {
    execute() {
        UserDao userDao = UserFactory.getDao();
        userDao.add();
    }
}

class UserFactory {
    private static UserDao userDao = new UserDao();
    
    public static UserDao getDao(){
        return userDao; // 返回同一个实例
    }
}

class UserDao {
    add() {
        // CODE...
    }
}

2.1.2 xml 解析

ioc 过程

第一步:xml 配置文件,配置创建的对象

<bean id="UserDao" class="com.lantz.UserDao"></bean>

第二步:假设现在有了UserServiceUserDao类,创建工厂类

public class UserFactory {
    public static UserDao getDao(){
        String classValue = class属性值;
        Class clazz = Class.forName(classValue);
        return (UserDao)clazz.newInstance();
    }
}

至此,进一步降低耦合度

因此,IOC的底层基于 IOC容器完成,IOC 的底层思想就是创建工厂,利用工厂模式降低耦合度

3、Spring 提供给 IOC 的两个接口

BeanFactory
  • IOC容器基本实现,Spring 内部实现的接口,不提供给开发人员使用
  • 加载配置文件的时候不会创建对象,只有获取对象的时候(使用)才会创建对象

ApplicationContext(推荐)

  • BeanFactory接口的子接口,提供了更多的更强大的功能,一般由开发人员直接使用
  • 加载配置文件的时候就会把配置文件对象进行创建

4、DI 依赖注入

依赖注入的概念

DI(Dependency Injection)依赖注入:

  • 在容器中建立 bean 与 bean 之间的依赖关系的整个过程,即依赖注入

依赖注入指的是:当一个对象依赖于其他对象(或数据)时,由Spring 容器负责在运行的时候将这些依赖注入进去,而不是由对象本身去查找或创建

一句话:我要什么就由容器给,不用自己弄(new)

代码演示

数据层实现

public class BookDaoImpl implements BookDao{
    @Override
    public void save() {
        System.out.println("book dao impl ...");
    }
}

业务层实现

public class BookServiceImpl implements BookService{

    private BookDao bookDao;

    @Override
    public void save() {
        bookDao.save();
    }
}

图示:

整体图示:

img

IOC容器内

img

4.1 依赖注入方式

我们在向一个类传递数据的方式主要是两种:set方法、构造方法。依赖注入描述了在容器中建立了beanbean之间依赖关系的过程,bean运行需要的类型有:引用类型,简单类型(基本数据类型与string)

setter 注入

通过类的setter方法完成依赖注入

简单类型

<bean id="student" class="com.example.Student">
    <property name="name" value="Tom"/>
    <property name="age" value="20"/>
</bean>
public class Student {
    private String name;
    private int age;

    // setter方法
    public void setName(String name) { this.name = name; }
    public void setAge(int age) { this.age = age; }
}

引用类型

<bean id="school" class="com.example.School">
    <property name="name" value="清华大学"/>
</bean>

<bean id="student" class="com.example.Student">
    <property name="name" value="Tom"/>
    <property name="school" ref="school"/>
</bean>

ref用于注入引用类型(bean

public class Student {
    private String name;
    private School school;

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

构造器注入(推荐)

通过类的构造方法来完成依赖项注入(推荐)

简单类型

<bean id="student" class="com.example.Student">
    <constructor-arg name="name" value="Tom"/>
    <constructor-arg name="age" value="20"/>
</bean>
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

引用类型

<bean id="school" class="com.example.School">
    <constructor-arg name="name" value="清华大学"/>
</bean>

<bean id="student" class="com.example.Student">
    <constructor-arg ref="school"/>
    <constructor-arg value="Tom"/>
</bean>
public class Student {
    private School school;
    private String name;

    public Student(School school, String name) {
        this.school = school;
        this.name = name;
    }
}

5、依赖自动装配

5.1 什么自动装配

自动装配是 Spring 用来自动满足 bean 之间依赖关系的一种机制

5.2 自动装配的几种方式

含义 装配方式 说明 适用场景
no(默认) 不自动装配 所有依赖都需要手动使用 <property><constructor-arg> 指定 控制最严格,适合显式配置
byName(不推荐) 属性名 自动装配 Setter 方法 容器中存在 与属性名相同 ID 的 bean 时自动注入 当 bean 命名规范统一时很方便
byType(推荐) 类型 自动装配 Setter 方法 容器中存在 与属性类型匹配的 bean 时自动注入;若有多个同类型 bean 会报错 当同类型 bean 唯一时最常用
constructor 构造方法参数类型 自动装配 构造器 容器会根据构造函数参数类型自动选择匹配的 bean 进行注入 构造器注入场景
default 使用上级 <beans> 元素的默认设置 依赖 <beans default-autowire=""> <beans> 设置了默认装配方式,则该 bean 自动继承该策略 批量统一配置 bean 的装配策略

代码模拟

xml 配置

<bean id="bookDao" class="com.springbean.dao.impl.BookDaoImpl"/>

<bean id="bookService" class="com.springbean.service.impl.BookServiceImpl" autowire="byType"/>

bookDao 实现类

public class BookDaoImpl implements BookDao {

    private int bookName;

    public void setBookName(int bookName) {
        this.bookName = bookName;
    }

    @Override
    public void save() {
        System.out.println("保存书籍...");
    }

}

bookService 实现类

public class BookServiceImpl implements BookService {

    private BookDao bookDao;

    public BookServiceImpl() {
        System.out.println("service constructor...");
    }

    @Override
    public void schedule() {
        System.out.println("预定了一本书...");
        bookDao.save();
    }

    public void setBookDao(BookDao bookDao) {
        System.out.println("service set ...");
        this.bookDao = bookDao;
    }

}

测试函数

public class BookMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        applicationContext.registerShutdownHook();

        BookService bookService = (BookService) applicationContext.getBean("bookService");
        bookService.schedule();
    }
}

BookServiceImpl 里有一个 BookDao 类型的属性

容器中存在一个 BookDaoImpl(类型匹配):

✅ 所以自动把 bookDao 注入到 bookService 里,就不要在测试函数添加一下这句了

BookDao bookDao = (BookDao) applicationContext.getBean("bookDao");
posted @ 2025-11-03 13:29  Lantz12  阅读(8)  评论(0)    收藏  举报