3、Spring 之IOC 容器 详解

这篇具有很好参考价值的文章主要介绍了3、Spring 之IOC 容器 详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。

IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。

1、IoC容器

1.1、控制反转(IoC)

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

  • 控制反转是一种思想。

  • 控制反转是为了降低程序耦合度,提高程序扩展力。

  • 控制反转,反转的是什么?

    • 将对象的创建权利交出去,交给第三方容器负责。
    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
  • 控制反转这种思想如何实现呢?

    • DI(Dependency Injection):依赖注入

1.2、依赖注入

DI(Dependency Injection):依赖注入,依赖注入实现了控制反转的思想。

依赖注入:

  • 指Spring创建对象的过程中,将对象依赖属性通过配置进行注入

依赖注入常见的实现方式包括两种:

  • 第一种:set注入
  • 第二种:构造注入

所以结论是:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。

1.3、IoC容器在Spring的实现

Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:

①BeanFactory

这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

②ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

③ApplicationContext的主要实现类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

类型名 简介
ClassPathXmlApplicationContext 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContext ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

2、基于XML管理Bean

2.1 搭建子模块spring6-ioc-xml

①搭建模块

搭建方式如:spring-first

②引入配置文件

引入spring-first模块配置文件:beans.xml、log4j2.xml

③添加依赖

<dependencies>
        <!--spring context依赖-->
        <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.11</version>
        </dependency>

        <!--junit5测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.9.2</version>
        </dependency>

        <!--log4j2的依赖-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.20.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.20.0</version>
        </dependency>
    </dependencies>

④引入java类

引入spring-first模块java实体类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.2 实验一:获取bean

①方式一:根据id获取

由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。前面 我们的spring 入门用的就是这种方式。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

②方式二:根据类型获取

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

③方式三:根据id和类型

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

④注意的地方

当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个

当IOC容器中一共配置了两个:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

然后我们根据类型进行获取Bean。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

就会抛出异常。

⑤扩展知识

如果组件类实现了接口,根据接口类型可以获取 bean 吗?

可以,前提是bean唯一

package com.jie.ioc.service;

/**
 * @Auther: Administrator
 * @Date: 2023/08/30/15:38
 * @Description:
 */
public interface HelloWorldService {

     void run();

}
package com.jie.ioc.service.impl;

import com.jie.ioc.HelloWorldTest;
import com.jie.ioc.service.HelloWorldService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author JIE
 * @version 1.0
 * @description: TODO
 * @date 2023/8/30 15:39
 */
public class HelloWorldServiceImpl implements HelloWorldService {

    private final Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);

    @Override
    public void run() {
        logger.info("方法调用");
    }
}

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?

不行,因为bean不唯一

package com.jie.ioc.service.impl;

import com.jie.ioc.HelloWorldTest;
import com.jie.ioc.service.HelloWorldService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author JIE
 * @version 1.0
 * @description: 第二个实现类
 * @date 2023/8/30 15:39
 */
public class HelloWorldServiceImpl2 implements HelloWorldService {

    private final Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);

    @Override
    public void run() {
        logger.info("方法调用");
    }
}

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

结论

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

java中,instanceof运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回true,否则返回false。也就是说:用instanceof关键字做判断时, instanceof 操作符的左右操作必须有继承或实现关系

2.3 实验二:依赖注入之setter注入

①创建学生类Student

package com.jie.ioc.model;

public class Student {

    private Integer id;

    private String name;

    private Integer age;

    private String sex;

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

}

②配置bean时为属性赋值

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="张三"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
</bean>

③测试

@Test
public void testDIBySet(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    Student studentOne = ac.getBean("studentOne", Student.class);
    System.out.println(studentOne);
}

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.4 实验三:依赖注入之构造器注入

①在Student类中添加有参构造

public Student(Integer id, String name, Integer age, String sex) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.sex = sex;
}

②配置bean

spring-di.xml

  <bean id="studentTwo" class="com.jie.ioc.model.Student">
        <constructor-arg value="1002"/>
        <constructor-arg value="李四"/>
        <constructor-arg value="33"/>
        <constructor-arg value=""/>
    </bean>

注意:

constructor-arg标签还有两个属性可以进一步描述构造器参数:

  • index属性:指定参数所在位置的索引(从0开始)
  • name属性:指定参数名

③测试

@Test
public void testDIByConstructor(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    Student studentOne = ac.getBean("studentTwo", Student.class);
    System.out.println(studentOne);
}

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.5 实验四:特殊值处理

①字面量赋值

什么是字面量?

int a = 10;

声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。

而如果a是带引号的:‘a’,那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。

<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>

②null值

<property name="name">
    <null />
</property>

注意:

<property name="name" value="null"></property>

以上写法,为name所赋的值是字符串null

③xml实体

<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>

④CDATA节

<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>

2.6、实验五:为对象类型属性赋值

①创建班级类Clazz

package com.jie.ioc.model;
    
public class Clazz {

    private Integer clazzId;

    private String clazzName;

    public Integer getClazzId() {
        return clazzId;
    }

    public void setClazzId(Integer clazzId) {
        this.clazzId = clazzId;
    }

    public String getClazzName() {
        return clazzName;
    }

    public void setClazzName(String clazzName) {
        this.clazzName = clazzName;
    }

    @Override
    public String toString() {
        return "Clazz{" +
                "clazzId=" + clazzId +
                ", clazzName='" + clazzName + '\'' +
                '}';
    }

    public Clazz() {
    }

    public Clazz(Integer clazzId, String clazzName) {
        this.clazzId = clazzId;
        this.clazzName = clazzName;
    }
}

②修改Student类

在Student类中添加以下代码:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

private Clazz clazz;

public Clazz getClazz() {
	return clazz;
}

public void setClazz(Clazz clazz) {
	this.clazz = clazz;
}

方式一:引用外部bean

配置Clazz类型的bean:

<bean id="clazzOne" class="com.jie.ioc.model.Clazz">
        <property name="clazzId" value="1111"/>
        <property name="clazzName" value="财源滚滚班"/>
</bean>

为Student中的clazz属性赋值:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 -->
    <property name="clazz" ref="clazzOne"/>
</bean>

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

记得重新生成一下toString 方法。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

错误演示:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

如果错把ref属性写成了value属性,会抛出异常: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘studentOne’ defined in class path resource [bean.xml]: Failed to convert property value of type ‘java.lang.String’ to required type ‘com.jie.ioc.model.Clazz’ for property ‘clazz’; Cannot convert value of type ‘java.lang.String’ to required type ‘com.jie.ioc.model.Clazz’ for property ‘clazz’: no matching editors or conversion strategy found

意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值

方式二:内部bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <property name="clazz">
        <!-- 在一个bean中再声明一个bean就是内部bean -->
        <!-- 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 -->
        <bean class="com.jie.ioc.model.Clazz">
            <property name="clazzId" value="2222"/>
            <property name="clazzName" value="远大前程班"/>
        </bean>
    </property>
</bean>

测试结果:
3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

方式三:级联属性赋值

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <property name="clazz" ref="clazzOne"/>
    <property name="clazz.clazzId" value="3333"/>
    <property name="clazz.clazzName" value="最强王者班"/>
</bean>

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.7 实验六:为数组类型属性赋值

①修改Student类

在Student类中添加以下代码:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

private String[] hobbies;

public String[] getHobbies() {
    return hobbies;
}

public void setHobbies(String[] hobbies) {
    this.hobbies = hobbies;
}

②配置bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <property name="clazz" ref="clazzOne"/>
    <property name="clazz.clazzId" value="3333"/>
    <property name="clazz.clazzName" value="最强王者班"/>
    <property name="hobbies">
        <array>
            <value>抽烟</value>
            <value>喝酒</value>
            <value>烫头</value>
        </array>
    </property>
</bean>

测试结果:

记得更改一下toString。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.8 实验七:为集合类型属性赋值

①为List集合类型属性赋值

在Clazz类中添加以下代码:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

private List<Student> students;

public List<Student> getStudents() {
    return students;
}

public void setStudents(List<Student> students) {
    this.students = students;
}

配置bean:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

 <bean id="clazzOne" class="com.jie.ioc.model.Clazz">
        <property name="clazzId" value="1111"/>
        <property name="clazzName" value="财源滚滚班"/>
        <property name="students">
            <list>
                <ref bean="studentTwo"/>
                <ref bean="studentOne"/>
            </list>
        </property>
    </bean>

测试结果:
3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

若为Set集合类型属性赋值,只需要将其中的list标签改为set标签即可

②为Map集合类型属性赋值

创建教师类Teacher:

package com.atguigu.spring6.bean;
public class Teacher {

    private Integer teacherId;

    private String teacherName;

    public Integer getTeacherId() {
        return teacherId;
    }

    public void setTeacherId(Integer teacherId) {
        this.teacherId = teacherId;
    }

    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }

    public Teacher(Integer teacherId, String teacherName) {
        this.teacherId = teacherId;
        this.teacherName = teacherName;
    }

    public Teacher() {

    }
    
    @Override
    public String toString() {
        return "Teacher{" +
                "teacherId=" + teacherId +
                ", teacherName='" + teacherName + '\'' +
                '}';
    }
}

在Student类中添加以下代码:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

private Map<String, Teacher> teacherMap;

public Map<String, Teacher> getTeacherMap() {
    return teacherMap;
}

public void setTeacherMap(Map<String, Teacher> teacherMap) {
    this.teacherMap = teacherMap;
}

配置bean:

<bean id="teacherOne" class="com.jie.ioc.model.Teacher">
    <property name="teacherId" value="10010"/>
    <property name="teacherName" value="大宝"/>
</bean>

<bean id="teacherTwo" class="com.jie.ioc.model.Teacher">
    <property name="teacherId" value="10086"/>
    <property name="teacherName" value="二宝"/>
</bean>

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <property name="hobbies">
        <array>
            <value>抽烟</value>
            <value>喝酒</value>
            <value>烫头</value>
        </array>
    </property>
    <property name="teacherMap">
        <map>
            <entry key="10010" value-ref="teacherOne"/>
            <entry key="10086" value-ref="teacherTwo"/>
        </map>
    </property>
</bean>

测试结果:
3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

③引用集合类型的bean

我们要使用到util:list、util:map标签必须引入相应的命名空间

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

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<!--list集合类型的bean-->
<util:list id="students">
    <ref bean="studentOne"/>
    <ref bean="studentTwo"/>
</util:list>
<!--map集合类型的bean-->
<util:map id="teacherMap">
    <entry key="10010" value-ref="teacherOne"/>
    <entry key="10086" value-ref="teacherTwo"/>
</util:map>

<bean id="studentOne" class="com.jie.ioc.model.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"/>
    <property name="name" value="null"/>
    <property name="age" value="23"/>
    <property name="sex" value=""/>
    <property name="hobbies">
        <array>
            <value>抽烟</value>
            <value>喝酒</value>
            <value>烫头</value>
        </array>
    </property>
    <property name="teacherMap" ref="teacherMap"/>
</bean>

<bean id="clazzOne" class="com.jie.ioc.model.Clazz">
    <property name="clazzId" value="1111"/>
    <property name="clazzName" value="财源滚滚班"/>
    <property name="students" ref="students"/>
</bean>

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.9 实验八:p命名空间

首先解释一下什么叫命名空间,或者说叫名称空间给大家做说明。

大家注意,在 “实验七:为集合类型属性赋值” 中的 引用集合类型的bean 演示过程中。我们增加了

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

这部分,它的名字 是 util, 我这里再加一个。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

比如说你看我现在写一个叉xmlns,大家注意,现在这个标签里边,它的属性名字是不是都叫xmlns这个名字。而现在如果这么写肯定不对的!

因为你不能有两个相同名称属性,所以咱们可以加上一个部分。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

比如说加一个P这就叫命名空间。各位可以简单理解为避免标签中属性定义的冲突。那它后面加上这么一个地址,完成之后下面再用它来完成一个注入的过程。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

完成之后咱们快速做个测试,试一下这个效果。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.10 实验九:引入外部属性文件

接下来我们来学习引入外部属性文件。

首先说一下这个引入外部属性文件的一个需求,或者它的应用场景是怎么样。大家注意,就是我现在的各种属注入,咱们是都写到了差不多文件中。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

如果说你只有一个bean注入,那没有关系。但是我们很多时候不可能只有一个bean。

这个时候大家发现我们这里面有很多的bean,有很多的值得注入。那咱们要再进行修改,要再进行维护,是不是就特别不方便。

所以实际中咱一般这么来做,把一些特定的固定值放到一个外部文件中,然后引入外文件在里面进行注入。

而比较常见的那就是数据库配置。比如说我们数据里边有他的用户名,有密码,有地址等信息。那咱们写一个外部文件,把文件引入,在里边注入,这样的话方便咱们进行维护。如果说我现在要改数据库,那咱就改我这个外部文件。而spring的插入文件,那咱就不需要修改,这就是它的一个基本的需求。

接下来我们来做一个实现。

①加入依赖

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

<!-- 数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.16</version>
</dependency>

②创建外部属性文件

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

jdbc.user=root
jdbc.password=ajie
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

③引入属性文件

引入context 名称空间

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<?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 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

注意:在使用 context:property-placeholder 元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

④配置bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

⑤测试

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

@Test
public void testDataSource() throws SQLException {
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.jdbc.xml");
    DataSource dataSource = ac.getBean(DataSource.class);
    DruidDataSource bean = ac.getBean(DruidDataSource.class);
    System.out.println(bean.getUrl());
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
}

2.11 实验十:bean的作用域

①概念

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

取值 含义 创建对象的时机
singleton(默认) 在IOC容器中,这个bean的对象始终为单实例 IOC容器初始化时
prototype 这个bean在IOC容器中有多个实例 获取bean时

如果是在WebApplicationContext环境下还会有另外几个作用域(但不常用):

取值 含义
request 在一个请求范围内有效
session 在一个会话范围内有效

②创建类User

package com.jie.ioc.model;

/**
 * 用户实体类
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 14:33
 */
public class User {

    private Integer id;

    private String username;

    private String password;

    private Integer age;

    public User() {
    }

    public User(Integer id, String username, String password, Integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}

③配置bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<?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">

    <!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
    <!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
    <bean id="user" class="com.jie.ioc.model.User" scope="prototype"/>

</beans>

④测试

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

这是多例模式,创建出来的是两个不同的对象,我们换成单例试试看。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

@Test
public void testBeanScope(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
    User user1 = ac.getBean(User.class);
    User user2 = ac.getBean(User.class);
    System.out.println(user1==user2);
}

2.12 实验十一:bean生命周期

什么叫生命周期?如果说一个人的生命周期,那就是从这个人他出生到死亡这个过程。如果说咱们现在说Bean的生命周期,那指的就是bean对象从创建到销毁的过程,这个叫生命周期。

①具体bean的生命周期过程

  • bean对象创建(调用无参构造器)

  • 给bean对象设置属性

  • bean的后置处理器(初始化之前)

  • bean对象初始化(需在配置bean时指定初始化方法)

  • bean的后置处理器(初始化之后)

  • bean对象就绪可以使用

  • bean对象销毁(需在配置bean时指定销毁方法)

  • IOC容器关闭

②修改类User

package com.jie.ioc.model;

/**
 * 用户实体类
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 14:33
 */
public class User {

    private Integer id;

    private String username;

    private String password;

    private Integer age;

    public User() {
        System.out.println("生命周期:1、创建对象");
    }

    public User(Integer id, String username, String password, Integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println("生命周期:2、依赖注入");
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

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

    public void initMethod(){
        System.out.println("生命周期:3、初始化");
    }

    public void destroyMethod(){
        System.out.println("生命周期:5、销毁");
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}

注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法

③配置bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<!-- 使用init-method属性指定初始化方法 -->
<!-- 使用destroy-method属性指定销毁方法 -->
<bean id="user" class=" com.jie.ioc.model.User" init-method="initMethod" destroy-method="destroyMethod">
    <property name="id" value="1001"/>
    <property name="username" value="admin"/>
    <property name="password" value="123456"/>
    <property name="age" value="23"/>
</bean>

④测试

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

@Test
public void testLife() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.lifecycle.xml");
    User bean = ac.getBean("user", User.class);
    System.out.println("生命周期:4、通过IOC容器获取bean并使用");
    System.out.println(bean);
    ac.close();
}

⑤bean的后置处理器

bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

创建bean的后置处理器:

package com.jie.ioc.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 15:34
 */
public class MyBeanProcessor implements BeanPostProcessor {


    /**
     * 初始化之前执行
     *
     * @param bean
     * @param beanName
     * @return: java.lang.Object
     * @author 阿杰 2416338031@qq.com
     * @date: 2023/9/3 15:37
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean的后置处理器(初始化之前执行)");
        System.out.println("☆☆☆" + beanName + " = " + bean);
        return bean;
    }

    /**
     * 初始化之后执行
     *
     * @param bean
     * @param beanName
     * @return: java.lang.Object
     * @author 阿杰 2416338031@qq.com
     * @date: 2023/9/3 15:37
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean的后置处理器(初始化之后执行)");
        System.out.println("★★★" + beanName + " = " + bean);
        return bean;
    }
}

在IOC容器中配置后置处理器:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.atguigu.spring6.process.MyBeanProcessor"/>

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

2.13 实验十二:FactoryBean

我们继续往下演示。咱们现在演示这么多都是基于xml 里边的bean管理。那下面咱们演示什么呢?它叫做FactoryBean。

①简介

FactoryBean是Spring提供的一种整合第三方框架的常用机制。

和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

②创建类UserFactoryBean

package com.jie.ioc.model;

import org.springframework.beans.factory.FactoryBean;

/**
 * 工厂Bean
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 15:48
 */
public class UserFactoryBean implements FactoryBean<User> {

    /**
     * 返回对象
     *
     * @return User
     */
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    /**
     * 返回对象类型
     *
     * @return Class<User>
     */
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

③配置bean

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

<bean id="user" class="com.atguigu.spring6.bean.UserFactoryBean"></bean>

④测试

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

这过程中他建了一个单实例的user对象,而不是我们配置这个 UserFactoryBean 的这个对象。

@Test
public void testUserFactoryBean(){
    //获取IOC容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.factorybean.xml");
    User user = (User) ac.getBean("user");
    System.out.println(user);
}

2.14 实验十三:基于xml自动装配

自动装配:

根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值

①场景模拟

创建类UserController

package com.jie.ioc.controller;

/**
 *  用户控制器 
 *
 * @author 阿杰 2416338031@qq.com
 * @date 2023/9/3 16:16
 * @version 1.0
*/
public class UserController {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void saveUser(){
        userService.saveUser();
    }

}

创建接口UserService

package com.jie.ioc.service;

/**
 *  用户服务 
 *
 * @author 阿杰 2416338031@qq.com
 * @date 2023/9/3 16:16
 * @version 1.0
*/
public interface UserService {

    void saveUser();

}

创建类UserServiceImpl实现接口UserService

package com.jie.ioc.service.impl;

import com.jie.ioc.service.UserService;

/**
 *   用户服务实现类 
 *
 * @author 阿杰 2416338031@qq.com
 * @date 2023/9/3 16:16
 * @version 1.0
*/
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void saveUser() {
        userDao.saveUser();
    }

}

创建接口UserDao

package com.jie.ioc.dao;

/**
 * 用户接口
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 16:17
 */
public interface UserDao {

    void saveUser();

}

创建类UserDaoImpl实现接口UserDao

package com.jie.ioc.dao.impl;

import com.jie.ioc.dao.UserDao;

/**
 * 用户dao实现类
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 16:17
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void saveUser() {
        System.out.println("保存成功");
    }

}

②配置bean

使用bean标签的autowire属性设置自动装配效果

自动装配方式:byType

byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值

若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值null

若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException

<?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="userController" class="com.jie.ioc.controller.UserController" autowire="byType" />


    <bean id="userService" class="com.jie.ioc.service.impl.UserServiceImpl" autowire="byType"/>

    <bean id="userDao" class="com.jie.ioc.dao.impl.UserDaoImpl"/>

</beans>

自动装配方式:byName

byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 根据名称做自动装配 -->
    <bean id="userController" class="com.jie.ioc.controller.UserController" autowire="byName"/>

    <bean id="userService" class="com.jie.ioc.service.impl.UserServiceImpl" autowire="byName"/>
    <bean id="userServiceImpl" class="com.jie.ioc.service.impl.UserServiceImpl" autowire="byName"/>

    <bean id="userDao" class="com.jie.ioc.dao.impl.UserDaoImpl"/>
    <bean id="userDaoImpl" class="com.jie.ioc.dao.impl.UserDaoImpl"/>

</beans>

③测试

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

@Test
void testAutoWireByXml(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
    UserController userController = ac.getBean(UserController.class);
    userController.saveUser();
}

3、基于注解管理Bean(☆)

从 Java 5 开始,Java 增加了对注解(Annotation)的支持,什么叫注解呢?大家可以理解为注解是代码中的一种特殊的标记。

可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。

Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。

Spring 通过注解实现自动装配的步骤如下:

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义 Bean
  4. 依赖注入

3.1 搭建子模块spring6-ioc-annotation

①搭建模块

搭建方式如:spring6-ioc-xml

②引入配置文件

引入spring-ioc-xml模块日志log4j2.xml

③添加依赖

<dependencies>
    <!--spring context依赖-->
    <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.11</version>
    </dependency>

    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.9.2</version>
    </dependency>

    <!--log4j2的依赖-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.20.0</version>
    </dependency>
</dependencies>

3.2 开启组件扫描

Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。

开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束(命名空间)。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

 <?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>

情况一:最基本的扫描方式

<!-- 开启组件扫描 -->
<context:component-scan base-package="com.jie.annotation"/>

这样只要是建在 annotation 及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

情况二:指定要排除的组件

<context:component-scan base-package="com.jie.annotation">
    <!-- context:exclude-filter标签:指定排除规则 -->
    <!-- 
             type:设置排除或包含的依据
            type="annotation",根据注解排除,expression中设置要排除的注解的全类名
            type="assignable",根据类型排除,expression中设置要排除的类型的全类名
        -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>

情况三:仅扫描指定组件

<context:component-scan base-package="com.jie.annotation" use-default-filters="false">
    <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
    <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
    <!--
             type:设置排除或包含的依据
            type="annotation",根据注解排除,expression中设置要排除的注解的全类名
            type="assignable",根据类型排除,expression中设置要排除的类型的全类名
        -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>

一般情况下,我们使用第一种就足够了。

3.3 使用注解定义 Bean

Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。

注解 说明
@Component 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

3.4 实验一:@Autowired注入

单独使用@Autowired注解,默认根据类型装配。【默认是byType】

查看源代码:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

在源码中大家看框起来的部分,这是什么呢?你可以理解为它叫元注解,就是规定的一个注解,它里边具体的一些细节部分。我看第一部分,它叫 @Target ,他表示这注解可以用在什么地方。

例如 @Autowired 可以用在:

  • 构造方法上
  • 方法上
  • 形参上
  • 属性上
  • 注解上

然后第二个地方,该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。

①场景一:属性注入

创建UserDao接口

package com.jie.annotation.dao;

/**
 * 用户dao接口
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 18:02
 */
public interface UserDao {

    public void print();
}

创建UserDaoImpl实现

package com.jie.annotation.dao.impl;


import com.jie.annotation.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * 用户dao实现类
 */
@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}

创建UserService接口

package com.jie.annotation.service;

/**
 *  用户服务接口 
 *
 * @author 阿杰 2416338031@qq.com
 * @date 2023/9/3 18:03
 * @version 1.0
*/
public interface UserService {

    public void out();
}

创建UserServiceImpl实现类

package com.jie.annotation.service.impl;


import com.jie.annotation.dao.UserDao;
import com.jie.annotation.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 *  服务层实现类 
 *
 * @author 阿杰 2416338031@qq.com
 * @date 2023/9/3 18:03
 * @version 1.0
*/
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

创建UserController类

package com.jie.annotation.controller;


import com.jie.annotation.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 * 控制器层
 *
 * @author 阿杰 2416338031@qq.com
 * @version 1.0
 * @date 2023/9/3 18:04
 */
@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }

}

测试一

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

②场景二:set注入

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

修改UserController类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试:成功调用

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

③场景三:构造方法注入

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

修改UserController类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试:成功调用

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

④场景四:形参上注入

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

修改UserController类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试:成功调用

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

⑤场景五:只有一个构造函数,无注解

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试通过

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

当有参数的构造方法只有一个时,@Autowired注解可以省略。

说明:有多个构造方法时呢?

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试结果:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

⑥场景六:@Autowired注解和@Qualifier注解联合

添加dao层实现

package com.jie.annotation.dao.impl;


import com.jie.annotation.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoRedisImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Redis Dao层执行结束");
    }
}

测试:

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

错误信息中说:不能装配,UserDao这个Bean的数量等于2

怎么解决这个问题呢?当然要byName,根据名称进行装配了。

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

总结

  • @Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
  • 当带参数的构造方法只有一个,@Autowired注解可以省略。()
  • @Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

3.5 实验二:@Resource注入

@Resource注解也可以完成属性注入。那它和**@Autowired**注解有什么区别?

  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
  • @Autowired注解是Spring框架自己的。
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
  • @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
  • @Resource注解用在属性上、setter方法上。
  • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

①场景一:根据name注入

修改UserDaoImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试通过

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

②场景二:name未知注入

修改UserDaoImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

修改UserServiceImpl类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试通过

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名。

③场景三 其他情况

修改UserServiceImpl类,myUserDao1属性名不存在

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试异常

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

根据异常信息得知:显然当通过name找不到的时候,自然会启动byType进行注入,以上的错误是因为UserDao接口下有两个实现类导致的。所以根据类型注入就会报错。

@Resource的set注入可以自行测试

总结

@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个

3.6 Spring全注解开发

全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端

测试类

3、Spring 之IOC 容器 详解,Spring系列,spring,java,IOC,Spring IOC,Spring 容器,spring IOC,后端文章来源地址https://www.toymoban.com/news/detail-700632.html

到了这里,关于3、Spring 之IOC 容器 详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一篇文章带你搞懂spring6的概念、spring入门与容器IoC详解(尚硅谷笔记)

    Spring 是一款主流的 Java EE 轻量级开源框架 ,Spring 由“Spring 之父”Rod Johnson 提出并创立,其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring 框架

    2023年04月16日
    浏览(37)
  • Spring IoC容器、IoC与DI

    目录 Spring是什么? 理解容器  什么是IoC(Inversion of Control) 传统的new创建对象的方式中类与类的耦合程度很大。  IoC的优势:  Spring IoC容器最核心的功能  什么是DI (Dependency Injection) IoC和DI的区别  Spring是指Spring Framework(Spring框架),它是开源的框架,有着很庞大的社区,通过

    2023年04月21日
    浏览(95)
  • Spring——Spring是什么?IoC容器是什么?

    本人是一个普通程序猿!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果你也对编程感兴趣的话,互关一下,以后互相学习,共同进步。这篇文章能够帮助到你的话,劳请大家点赞转发支持一下! 我们通常所说的 Spring 指的是 Spring Framework(Spring 框架) ,它是

    2024年02月15日
    浏览(36)
  • spring ioc容器

    ioc是 inversion of Control的简写,意为控制反转。通过其对所有的Java对象的实例化和初始化,控制对象与对象之间的依赖关系。 (1)控制反转是一种思想。 (2)控制反转是为了 降低程序耦合度,提高程序扩展力。 (3)控制反转,反转的是什么? 答:将对象的创建权利交出去

    2024年01月17日
    浏览(36)
  • Spring 6.X IoC 容器

    下面主要介绍 Spring 框架对控制反转 (IoC) 原理的实现 首先要说明的是:IoC 也称为依赖注入,这是一个过程。 其次依赖项的定义:对象仅通过构造函数参数、工厂方法的参数,或在构造对象实例、工厂方法返回后在对象实例上设置的属性来定义其依赖项(即它们使用的其他对

    2024年02月09日
    浏览(37)
  • Spring核心容器IOC案例讲解,带你理解IOC

    Universe Infinity inc. 什么是IOC容器,先把IOC给忽略到,其实就是个容器。 什么?容器又是个啥玩意?容器是用来放东西的东西啊。 各个领域都喜欢起一些专业术语,显得很高级。给你讲IOC是不是很高级,给你讲Map是不是就明白了。 bean对象最终存储在spring容器中,在spring源码底

    2024年01月24日
    浏览(37)
  • Spring 核心之 IOC 容器学习二

    Annotation 的前世今生 定位 Bean 扫描路径 读取 Annotation 元数据 扫描指定包并解析为 BeanDefinition

    2024年01月19日
    浏览(48)
  • Spring 核心之 IOC 容器学习一

    1、BeanFactory 其中 BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,BeanFactory 有三个重要的子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。 那

    2024年01月19日
    浏览(43)
  • spring6-IOC容器

    IOC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将

    2024年02月08日
    浏览(40)
  • spring6-实现简易版IOC容器

    我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。 1、回顾Java反射 Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包