Spring的IOC

在现代 Java 开发中,Spring 框架几乎无处不在,特别是其核心的 IOC(Inversion of Control) 容器,几乎所有Spring的功能都与它紧密相关。

一、什么是IOC

IOC,全称为 Inversion of Control(控制反转),顾名思义,它是将原本由程序员控制的对象创建、对象依赖关系的管理等责任反转给框架来做,程序员只需要编写业务逻辑,框架会处理对象的创建和管理。

在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况:比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类出了问题,那么可能会导致这个框架出现问题。

Spring 将创建对象的权利给了IOC,在IOC当中创建了ABC三个对象吗,那么我们我们其他的类只需要调用集合,大大的解决了程序耦合性的问题。

通过 IOC,Spring 容器负责创建和管理对象的生命周期,同时还负责将需要的依赖注入到这些对象中。这就避免了传统编程方式中,程序员需要手动管理对象实例化和依赖关系的问题。

举个通俗的例子:

  • 传统方式:你去餐厅吃饭,自己点菜、自己烹饪、自己上菜、自己清理桌面。
  • IOC方式:你去餐厅吃饭,餐厅的服务员负责点菜、烹饪、上菜,甚至清理桌面,你只需要享受美食。

这样看,IOC 就像一个得力的服务员,帮你解决了很多繁琐的事情,而你只需要专注于业务逻辑即可。

二、IOC的底层原理

在 Spring 中,IOC 主要由 BeanFactory 和 ApplicationContext 两个核心容器实现。BeanFactory 是 Spring 的基本容器,而 ApplicationContext 是 BeanFactory 的扩展,具备更多的功能。

IOC的实现主要依赖于:

  1. dom4j 解析 xml 文档
  2. 工厂模式
  3. 采用反射设计模式创建对象

1、工厂模式

在当前情况之下A类想要调用B类就必须自己在自己的内部新建B类的对象,这样的耦合度太高,如何降低耦合度的呢?

java 复制代码
public class A {
    public void run(){
        B b = AFactory.getB();
        b.print();
    }
}

public class B {
    public void print(){
        System.out.println("你好");
    }
}

public class BFactory {
    public static B getB(){
        return new B();
    }
}

创建一个工厂类BFactory,这样就能够使得A和B的耦合度降低到最小值

2、容器与 Bean 的管理

Spring 的 IOC 容器负责创建、管理和销毁应用中的所有 bean。每个 bean 都是由 Spring 容器根据配置创建的 Java 对象,并通过依赖注入(DI)来管理它们之间的依赖关系。

Bean管理指的是:

  1. 创建对象
  2. 注入操作

这两个操作

3、依赖注入(DI)

依赖注入是 IOC 的关键实现方式,Spring 提供了三种主要的 DI 方式:

  • 构造器注入:通过构造函数传递依赖。
  • Setter 注入:通过 setter 方法传递依赖。
  • 接口注入:通过接口传递依赖(这种方式较少使用,Spring 没有默认支持)。

4、 Bean 生命周期

Spring 容器中的 Bean 在创建时,会经历以下生命周期:

  • 实例化:根据配置实例化对象。
  • 填充属性:根据配置文件将属性注入到 Bean 中。
  • 初始化:初始化 Bean,执行自定义的初始化方法。
  • 销毁:当容器销毁时,执行销毁方法。

三、基于XML配置文件实现Bean管理和依赖注入

1、Maven项目信息配置

配置Maven项目基本信息,构建一个 Spring IOC 示例项目,并集成了日志和单元测试支持。

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?> 
<!-- 声明 XML 版本和编码格式,UTF-8 适用于多语言支持 -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- 定义项目的根元素 <project>,指定 POM 的 XML 命名空间
         xsi:schemaLocation 指定了 XML Schema 文件的位置,用于 POM 语法验证 -->

    <modelVersion>4.0.0</modelVersion>
    <!-- 指定 POM 的模型版本,当前 Maven 版本统一使用 4.0.0 -->

    <groupId>com.goose</groupId>
    <!-- 组 ID,通常采用公司或组织的反向域名,标识该项目属于哪个组织或团队 -->

    <artifactId>Spring_IOCDemo</artifactId>
    <!-- 工件 ID,代表当前项目的名称,最终生成的 JAR 或 WAR 文件会使用这个名称 -->

    <version>1.0-SNAPSHOT</version>
    <!-- 版本号,1.0-SNAPSHOT 表示开发中的快照版本,非正式发布 -->

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <!-- 指定 Java 源代码版本为 8 -->

        <maven.compiler.target>8</maven.compiler.target>
        <!-- 指定编译后的字节码版本为 Java 8 -->
    </properties>
    
    <dependencies>
        <!-- 依赖管理,列出项目所需的外部库 -->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- Spring Context 依赖,提供 IoC 容器和 AOP 等核心功能,版本 5.0.2.RELEASE -->

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- commons-logging 依赖,Spring 使用它作为日志抽象层,可适配多种日志框架 -->

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!-- log4j 依赖,具体的日志实现,用于输出日志信息 -->

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- JUnit 依赖,版本 4.12,scope 为 test,表示该依赖仅用于测试环境 -->
        
    </dependencies>
</project>
<!-- 结束 <project> 标签,完整定义 Maven 项目 -->

2、创建Spring依赖

用于定义和管理不同的 bean(对象)及其依赖关系,使用 XML 配置方式 来定义应用程序的对象,Spring 容器会根据这个配置文件来创建和管理对象的生命周期,并注入它们之间的依赖。

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

3、创建对象

Spring.xml中定义一个名为 car 的 Bean,并指定它的实现类为 com.goose.entity.Car。

XML 复制代码
id :类的唯一标识符 
class : 类的全路径名
<bean id="car" class="com.goose.entity.Car"/>

创建实体类 Car,方便观察,设计 run 方法来测试

java 复制代码
public class Car{
    public void run(){
        System.out.println("汽车轱辘骨碌碌的转.........");
    }
}

创建测试类

java 复制代码
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
    //传统方式创建对象
    @Test
    public void test(){
        // 创建对象
        Car car = new Car();
        // 调用方法
        car.run();
    }
    
    // Spring 创建对象
    @Test
    public void test_Spring(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
        Car car = (Car) ac.getBean("car");
        // 方法调用
        car.run();
    }
}

4、注入属性

1.普通属性

Spring.xml中定义一个名为 car 的 Bean,并指定它的实现类为 com.goose.entity.Car,并指定属性和对应值。ref 用于表示该属性的值是引用另一个已经定义的 Bean。

ref="engine":这表示 car Bean 的 engine 属性将会通过引用另一个已定义的 Bean 来注入,那个 Bean 的 ID 为 engine。Spring 会从容器中查找一个 ID 为 engine 的 Bean,并将其注入到 car Bean 的 engine 属性中。

换句话说,ref="engine" 告诉 Spring 容器,car 这个对象的 engine 属性应该被赋值为 ID 为 engine 的 Bean,而不是一个普通的值。

XML 复制代码
<bean id="car" class="com.goose.entity.Car">
    <property name="brand" value="Geely"/>
    <property name="IDs" value="18952"/>
    <property name="engine" ref="engine"/>
</bean>

<bean id="engine" class="com.goose.entity.Engine">
    <constructor-arg name="cylinder" value="V8"/>
    <constructor-arg name="displayment" value="6.0T"/>
    <constructor-arg name="ratio" value="49%"/>
</bean>

创建实体类 Car 和 Engine ,其中 Engine 中实现了使用带参构造方法进行值的注入,在 xml 中需要使用 constructor-arg 关键字。

java 复制代码
public class Car implements CarInterface {
    private Integer IDs;
    private String Brand;
    private Engine engine;

    @Override
    public String toString() {
        return "Car{" +
                "IDs=" + IDs +
                ", Brand='" + Brand + '\'' +
                ", engine=" + engine +
                '}';
    }

    public Integer getIDs() {
        return IDs;
    }

    public void setIDs(Integer IDs) {
        this.IDs = IDs;
    }

    public String getBrand() {
        return Brand;
    }

    public void setBrand(String brand) {
        Brand = brand;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void run(){
        System.out.println("汽车轱辘骨碌碌的转.........");
    }
}
java 复制代码
public class Engine {
    private String cylinder;
    private String displayment;
    private String ratio;

    public Engine(String cylinder, String displayment, String ratio) {
        this.cylinder = cylinder;
        this.displayment = displayment;
        this.ratio = ratio;
    }

    @Override
    public String toString() {
        return "Engine{" +
                "cylinder='" + cylinder + '\'' +
                ", displayment='" + displayment + '\'' +
                ", ratio='" + ratio + '\'' +
                '}';
    }

    public String getCylinder() {
        return cylinder;
    }

    public void setCylinder(String cylinder) {
        this.cylinder = cylinder;
    }

    public String getDisplayment() {
        return displayment;
    }

    public void setDisplayment(String displayment) {
        this.displayment = displayment;
    }

    public String getRatio() {
        return ratio;
    }

    public void setRatio(String ratio) {
        this.ratio = ratio;
    }
}

测试类

java 复制代码
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
    @Test
    public void test_Spring(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
        Car car = (Car) ac.getBean("car");
        // 方法调用
        car.run();
        System.out.println(car.toString());
    }
}

2.集合属性

Spring.xml中定义一个名为 bot 的 Bean,并指定它的实现类为 com.goose.entity.Bot。

XML 复制代码
<bean id="bot" class="com.goose.entity.Bot">
    <property name="list">
        <list>
            <value>小新</value>
            <value>小爱</value>
        </list>
    </property>
    <property name="map">
        <map>
            <entry key="1" value="广志"/>
            <entry key="2" value="美伢"/>

        </map>
    </property>
    <property name="strs">
        <array>
            <value>正男</value>
            <value>妮妮</value>
        </array>
    </property>
</bean>

创建实体类

java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class Bot {
    private String [] strs;
    private List<String> list;
    private Map<String,String> map;

    @Override
    public String toString() {
        return "Bot{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                '}';
    }

    public String[] getStrs() {
        return strs;
    }

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }
}

测试类

java 复制代码
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
    @Test
    public void test_Spring(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
        Bot bot = (Bot) ac.getBean("bot");
        // 集合等普通变量设值
        System.out.println(bot.toString());
    }
}

在构造器中注入

XML 复制代码
<bean name="bot2" class="com.goose.entity.Bot_2">
    <constructor-arg name="strs">
        <array>
            <value>正男</value>
            <value>妮妮</value>
        </array>
    </constructor-arg>
    <constructor-arg name="list">
        <list>
            <value>正男</value>
            <value>妮妮</value>
        </list>
    </constructor-arg>
    <constructor-arg name="map">
        <map>
            <entry key="1" value="广志"/>
            <entry key="2" value="美伢"/>
        </map>
    </constructor-arg>
</bean>
java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class Bot_2 {
    private String [] strs;
    private List<String> list;
    private Map<String,String> map;

    public Bot_2(String[] strs, List<String> list, Map<String, String> map) {
        this.strs = strs;
        this.list = list;
        this.map = map;
    }

    @Override
    public String toString() {
        return "Bot{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                '}';
    }

    public String[] getStrs() {
        return strs;
    }

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }
}
java 复制代码
Bot_2 bot_2 = (Bot_2) ac.getBean("bot2");
// 集合等构造器实现注入
System.out.println(bot_2.toString());

四、基于注解实现Bean管理和注入

1、什么是注解

①:注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)

②:使用注解,注解作用在类上面,方法上面,属性上边

③:使用注解的目的:简化XML配置

2、Spring针对Bean管理创建对象提供的注解

@Component 普通的类

@Controller 表现层

@Service 业务层

@Repository 持久层

*上边四个功能一样,都可以用来创建bean实例

3、用注解创建对象

1.编写接口和实现类

java 复制代码
public interface CarInterface {
    public void hello();
}
java 复制代码
package com.goose.entity;

import com.goose.config.CarInterface;
import org.springframework.stereotype.Component;
/* <bean id="us" class="UserServiceImpl"/> */
/**
 * 组件,作用:把当前类使用IOC容器进行管理,如果没有指定名称,默认使用类名,首字母是小写。
 * userServiceImpl。或者自己指定名称
 **/
@Component(value = "cars")
public class Car implements CarInterface {
    @Override
    public void hello() {
        System.out.println("俺是注解创建对象测试......");
    }
}

2.编写配置文件,开启注解扫描

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"
       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.goose"/>
</beans>

3.测试类

java 复制代码
@Test
public void test_Zhujie(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
    Car car = (Car) ac.getBean("cars");
    car.hello();
}

4、用注解注入属性

@Value 用于注入普通类型(String,int,double等类型)

@Autowired 默认按类型进行自动装配(引用类型)

@Qualifier 不能单独使用必须和@Autowired一起使用,强制使用名称注入

@Resource Java提供的注解,也被支持。使用name属性,按名称注入

1.编写实现类

java 复制代码
package com.goose.entity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "poss")
public class Possession {
    @Value("刘海柱")
    private String name;
    @Autowired
    private Car car;

    @Override
    public String toString() {
        return "Possession{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }
}
java 复制代码
package com.goose.entity;

import com.goose.config.CarInterface;
import org.springframework.stereotype.Component;

@Component(value = "cars")
public class Car implements CarInterface {
    private Integer IDs;
    private String Brand;
    private Engine engine;

    @Override
    public String toString() {
        return "Car{" +
                "IDs=" + IDs +
                ", Brand='" + Brand + '\'' +
                ", engine=" + engine +
                '}';
    }

    public Integer getIDs() {
        return IDs;
    }

    public void setIDs(Integer IDs) {
        this.IDs = IDs;
    }

    public String getBrand() {
        return Brand;
    }

    public void setBrand(String brand) {
        Brand = brand;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void run(){
        System.out.println("汽车轱辘骨碌碌的转.........");
    }

    @Override
    public void hello() {
        System.out.println("俺是注解创建对象测试......");
    }
}

2.测试类

java 复制代码
@Test
public void test_Zhuru(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
    Possession poss = (Possession) ac.getBean("poss");
    System.out.println(poss.toString());
}

五、不同方式的对比

|------|--------------------|---------------------|
| 特性 | 基于 XML 配置 | 基于注解配置 |
| 配置方式 | 使用 XML 文件进行配置 | 使用注解来标记类和依赖关系 |
| 易用性 | 配置较繁琐,尤其是 Bean 多时 | 配置简单,类和依赖关系直接通过注解指定 |
| 灵活性 | 灵活,适用于复杂配置 | 配置简洁,适用于大多数应用 |
| 可维护性 | XML 配置文件容易维护,但文件较大 | 代码更加简洁,方便修改 |

相关推荐
独行soc几秒前
2025年渗透测试面试题总结-某华为面试复盘 (题目+回答)
java·python·安全·web安全·面试·职场和发展·红蓝攻防
neoooo6 分钟前
Redis 缓存击穿、穿透、雪崩问题及解决方案
java·spring boot·redis
web1508541593511 分钟前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
半聋半瞎11 分钟前
Java单例模式中的饿汉模式和懒汉模式
java·javascript·单例模式
杨充15 分钟前
08.面向对象的特性
后端
小王C语言18 分钟前
【C++初阶】---类和对象(上)
java·开发语言·c++
bing_15827 分钟前
Nacos 项目是如何使用Maven 构建的?如何进行编译和打包?
java·服务发现
浅念同学41 分钟前
JavaEE-MyBatis概述&第一个程序
java·java-ee·mybatis
BigTopOne1 小时前
【Websocket 】带着问题来看(一)
后端
kkk哥1 小时前
基于springboot的校园资料分享平台(048)
java·spring boot·后端