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 配置文件容易维护,但文件较大 | 代码更加简洁,方便修改 |

相关推荐
system_error_x1 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事2 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客2 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1882 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn3 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su3 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
码事漫谈3 小时前
AI 读你的提示词,其实很没耐心
后端
用户8356290780514 小时前
如何使用 Python 格式化 Excel 单元格中的长文本
后端·python
我命由我123454 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
IT_陈寒4 小时前
Java性能优化完全指南
前端·人工智能·后端