获取IOC容器里bean的方式

694 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

本篇文章简单介绍了什么是IOC,通过例子说明了spring框架如何搭建。

1.什么是IOC

IOC:Inversion of Control,翻译过来是反转控制。 传统方式:我们都是主动的从容器里面获取组件等资源。这种方式意味着开发人员必须清楚的知道在容器里获取特定组件的步骤,而这样显然加大了开发人员的学习成本,降低了开发效率。

IOC方式:这种方式和传统方式是相反的,由容器主动推送相关的资源,开发人员只需提供接收资源的方式即可。这意味着开发人员不需要知道容器里组件的创建过程,降低了学习成本,提高了开发效率。

2.相关API介绍

Spring的IOC容器就是IOC思想的实现。IOC容器里的组件叫做bean。在获取bean之前,我们需要获取IOC容器,再通过IOC容器获取里面的bean

在Spring里,获取容器是借助Spring提供的API。接下来变介绍几个API。

首先创建一个Spring项目,过程很简单,就是创建一个maven项目,然后导入对应的依赖即可。

<packaging>jar</packaging>
<dependencies>
    <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

在idea里双击shift,搜索BeanFactory

image.png 进入BeanFactory接口后,按下Ctrl+h,可以查询当前接口的子接口和实现类

image.png

BeanFactory: 这是IOC容器的基本实现,是Spring内部使用的接口,意味着这接口不对开发人员开放。

ApplicationContext: 这是BeanFactory的子接口,提供更多的特性,面向开发者,几乎所有场合都使用它而不是BeanFactory。

ApplicationContext接口的实现类: 实现类有两个,分别是FileSystemXmlApplicationContext和ClassPathXmlApplicationContext。这两个实现类的作用都是去读取Spring的配置文件,从而获取容器。FileSystemXmlApplicationContext读取配置文件是从磁盘开始读取的,一般我们不用它,设想下你项目移动到了另一台电脑,你会发现配置文件的位置和原先的不一定一样。ClassPathXmlApplicationContext是从类路径下开始读取spring的配置文件。

3.实验验证

3.1 准备工作:

1.在idea用maven创建出一个基本框架

2.导入spring相关依赖,参考上面

3.在resources目录下创建spring的配置文件。在单纯的spring框架里面,这个配置文件叫什么名字都无所谓,在整合SSM时,这个配置文件的名字是固定的叫applicationContext.xml。下图是创建spring配置文件的方式。

image.png 4.创建实体类。实体类的无参构造方法是必须有的,有参构造方法可有可无。

public class Student {

    private Integer sid;

    private String sname;

    private Integer age;

    private String gender;
    
    //无参构造方法
    
    //有参构造方法
    //get和set方法
    //toString方法

5.往spring的配置文件里写入内容

<?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:配置一个bean对象(实体类对象),将对象交给IOC容器
    属性:
    id:bean的唯一标识,不能重复。设想下,那么多个对象,你要靠什么来将它们区分开来,显然用唯一的id比较方便
    class:设置bean对象所对应的类型。说白了,就是你这个bean的实体类在哪,是怎样的。
-->
    <bean id="studentOne" class="com.atguigu.spring.pojo.Student"></bean>

</beans>

3.2 同过id获取IOC容器里的bean

创建一个测试类。通过ClassPathXmlApplicationContext获取容器,接着利用在配置文件里指定的bean的id获取bean。

public class IOCByXMLTest {

    /**
     * 获取Bean的三种方式:
     * 1.根据bean的id获取
     * 2.根据bean的类型获取。
     * 注意:根据类型获取bean时,要求IOC容器中有且只有一个类型匹配的bean 不然会报NoUniqueBeanDefinitionException
     * 若没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException
     * 3.根据bean的id和类型获取
     */

    @Test
    public void testIOC(){
        //获取IOC容器
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        //获取bean
        Student studentOne = (Student) ioc.getBean("studentOne");
        System.out.println(studentOne);
    }

}

image.png

3.3 根据类型获取IOC容器里的bean

@Test
public void testIOC(){
    //获取IOC容器
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    //获取bean
    Student student = ioc.getBean(Student.class);
    System.out.println(student);
}

image.png 注意:如果是根据类型获取bean,那么前提应该是相同类型的bean只能有一个。 我们现在修改一下spring的配置文件,让它有两个相同类型的bean。

<bean id="studentOne" class="com.atguigu.spring.pojo.Student"></bean>
<bean id="studentTwo" class="com.atguigu.spring.pojo.Student"></bean>

再次运行代码,会报NoUniqueBeanDefinitionException错误。

如果spring容器里没有对应类型的bean,那会怎样? 我们可以将配置文件里的定义的bean都注释掉。

<!--<bean id="studentOne" class="com.atguigu.spring.pojo.Student"></bean>
<bean id="studentTwo" class="com.atguigu.spring.pojo.Student"></bean>-->

再次运行代码,会报NoSuchBeanDefinitionException错误。

3.4 根据id和类型获取IOC容器里的bean

@Test
public void testIOC(){
    //获取IOC容器
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    //获取bean
    Student student = ioc.getBean("studentOne", Student.class);
    System.out.println(student);
}

image.png

3.5 扩展-组件实现了接口,根据接口类型获取bean

如果组件实现了接口,那么我们是可以通过接口的类型获取bean的,这在实际工作中是最常见的。这个也是有前提条件的,那就是这个bean必须是唯一的

如果这个接口有多个实现类,这些实现类都配置了bean,我们是不能通过接口的类型来获取bean的,因为这时候的bean不唯一

<!--class只能写类,不要写接口哦.接口里面可没有构造方法-->
<bean id="studentOne" class="com.atguigu.spring.pojo.Student"></bean>
<!--<bean id="studentTwo" class="com.atguigu.spring.pojo.Student"></bean>-->
public interface Person {
}
public class Student implements Person {

    private Integer sid;

    private String sname;

    private Integer age;

    private String gender;
    
    //无参构造方法
    
    //有参构造方法
    //get和set方法
    //toString方法
@Test
public void testIOC(){
    //获取IOC容器
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    //获取bean
    Person student = ioc.getBean(Person.class);
    System.out.println(student);
}

image.png

4.类里面没有无参构造方法会怎样

我们现在可以尝试着将类里面的无参构造方法删除,接着运行代码,会报NoSuchMethodException。因为容器里的bean是通过工厂模式以及反射的方式创建的,所以我们需要类拥有一个无参构造方法。你可以这样想,无参构造方法是固定的,spring用反射的时候比较方便。

5.总结

  1. 获取bean的过程是通过ClassPathXmlApplicationContext读取spring配置文件,获取容器;接着通过容器获取bean
  2. 获取bean的方式有三种:1.通过bean的id获取bean;2.通过bean的类型获取bean,这种方式在实际工作中更常用,我们只需确保这个类型的bean只有一个;3.根据bean的id和bean的类型获取bean
  3. bean对应的类应该有无参构造方法,不然运行代码时会报错。这也从侧面告诉我们,在spring的配置文件中,定义bean的时候,class的类型不能是接口。