Spring IoC容器简介(新手必看)
IoC 容器是一个管理 Bean 的容器,在 Spring 的定义中,所有 IoC 容器都需要实现接口 BeanFactory,它是一个顶级容器接口。
为了增加对它的理解,我们首先阅读其源码,并讨论几个重要的方法,其源码如下:
1) getBean():这是 IoC 容器最重要的方法之一,它的作用是从 IoC 容器中获取 Bean。从多个 getBean() 方法中可以看到,有按名称(by name)获取 Bean 的,也有按类型(by type)获取 Bean 的,这就意味着在 IoC 容器中,允许我们按名称或者类型获取 Bean,这对理解 Spring 的依赖注入(dependency injection,DI)是十分重要的。
2) isSingleton():判断 Bean 是否在 IoC 容器中为单例。这里需要记住的是,在 IoC 容器中, Bean 默认都是以单例存在的,也就是使用 getBean() 方法根据名称或者类型获取的对象,在默认的情况下,返回的都是同一个对象。
3) isPrototype():与 isSingleton() 方法是相反的,如果它返回的是 true,那么当我们使用 getBean() 方法获取 Bean 的时候,IoC 容器就会创建一个新的 Bean 返回给调用者,这些与 Bean 的作用域相关。
由于 BeanFactory 接口定义的功能还不够强大,因此 Spring 在 BeanFactory 的基础上,还设计了一个更为高级的接口 ApplicationContext,它是 BeanFactory 的子接口之一。
在 Spring 的体系中,BeanFactory 和 ApplicationContext 是最为重要的接口设计,在现实中我们使用的大部分 IoC 容器是 ApplicationContext 接口的实现类。
BeanFactory 和 ApplicationContext 的关系如下图所示:

图 1 IoC 容器的接口设计
在图 1 中可以看到,ApplicationContext 接口通过扩展上级接口,进而扩展了 BeanFactory 接口,但是在 BeanFactory 的基础上,扩展了消息国际化接口(MessageSource)、环境可配置化接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析器接口(ResourcePatternResolver),所以 ApplicationContext 的功能会更为强大。
Spring Boot 主要通过注解来将 Bean 装配到 IoC 容器中,为了贴近 Spring Boot 的需要,这里不再介绍与 XML 相关的 IoC 容器,而主要介绍一个基于注解的 IoC 容器——AnnotationConfigApplicationContext。从这个类的名称就可以看出,它是一个基于注解的 IoC 容器,之所以研究它,是因为 Spring Boot 装配和获取 Bean 的方式如出一辙。
下面来看一个最为简单的例子。首先定义一个 Java简 单对象(plain ordinary Java object,POJO)文件 User.java:
然后定义一个 Java 配置文件 AppConfig.java,代码如下:
做好了这些,就可以使用 AnnotationConfigApplicationContext 来构建自己的 IoC 容器了,代码如下:
上述代码将 Java 配置文件 AppConfig.java 传递给 AnnotationConfigApplicationContext 的构造方法,这样就能创建 IoC 容器了。IoC 容器会根据 AppConfig 创建 Bean,然后将 Bean 装配进来,这样就可以使用 getBean() 方法获取对应的 Bean 了。
注意,Spring 在默认的情况下,扫描到 Bean 后就创建 Bean 并将 Bean 装配到 IoC 容器中,而不是使用 getBean() 方法后才创建和装配 Bean。运行代码后,可以打印出下面的日志:
为了增加对它的理解,我们首先阅读其源码,并讨论几个重要的方法,其源码如下:
package org.springframework.beans.factory; /**** imports ****/ public interface BeanFactory { // 前缀 String FACTORY_BEAN_PREFIX = "&"; // 多个getBean()方法 Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 两个获取Bean的提供器 <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); // 是否包含Bean boolean containsBean(String name); // Bean是否为单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // Bean是否为原型 boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 是否类型匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; // 获取Bean的类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException; // 获取Bean的别名 String[] getAliases(String name); }上述源码中加入了中文注释,通过这些中文注释就可以理解这些方法的含义了,下面我们再介绍一些重要的方法:
1) getBean():这是 IoC 容器最重要的方法之一,它的作用是从 IoC 容器中获取 Bean。从多个 getBean() 方法中可以看到,有按名称(by name)获取 Bean 的,也有按类型(by type)获取 Bean 的,这就意味着在 IoC 容器中,允许我们按名称或者类型获取 Bean,这对理解 Spring 的依赖注入(dependency injection,DI)是十分重要的。
2) isSingleton():判断 Bean 是否在 IoC 容器中为单例。这里需要记住的是,在 IoC 容器中, Bean 默认都是以单例存在的,也就是使用 getBean() 方法根据名称或者类型获取的对象,在默认的情况下,返回的都是同一个对象。
3) isPrototype():与 isSingleton() 方法是相反的,如果它返回的是 true,那么当我们使用 getBean() 方法获取 Bean 的时候,IoC 容器就会创建一个新的 Bean 返回给调用者,这些与 Bean 的作用域相关。
由于 BeanFactory 接口定义的功能还不够强大,因此 Spring 在 BeanFactory 的基础上,还设计了一个更为高级的接口 ApplicationContext,它是 BeanFactory 的子接口之一。
在 Spring 的体系中,BeanFactory 和 ApplicationContext 是最为重要的接口设计,在现实中我们使用的大部分 IoC 容器是 ApplicationContext 接口的实现类。
BeanFactory 和 ApplicationContext 的关系如下图所示:

图 1 IoC 容器的接口设计
在图 1 中可以看到,ApplicationContext 接口通过扩展上级接口,进而扩展了 BeanFactory 接口,但是在 BeanFactory 的基础上,扩展了消息国际化接口(MessageSource)、环境可配置化接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析器接口(ResourcePatternResolver),所以 ApplicationContext 的功能会更为强大。
Spring Boot 主要通过注解来将 Bean 装配到 IoC 容器中,为了贴近 Spring Boot 的需要,这里不再介绍与 XML 相关的 IoC 容器,而主要介绍一个基于注解的 IoC 容器——AnnotationConfigApplicationContext。从这个类的名称就可以看出,它是一个基于注解的 IoC 容器,之所以研究它,是因为 Spring Boot 装配和获取 Bean 的方式如出一辙。
下面来看一个最为简单的例子。首先定义一个 Java简 单对象(plain ordinary Java object,POJO)文件 User.java:
package com.learn.chapter.pojo; /**** imports ****/ public class User { private Long id; // 编号 private String userName; // 用户名 private String note; // 备注 /**setters and getters **/ }
然后定义一个 Java 配置文件 AppConfig.java,代码如下:
@Configuration public class AppConfig { @Bean(name = "user") public User initUser() { return new User(); } }
- @Configuration 表示这是一个 Java 配置类,Spring 的容器会根据它来生成 IoC 容器,从而去装配 Bean;
- @Bean 表示将 initUser() 方法返回的对象装配到 IoC 容器中,该方法的属性 name 表示这个 Bean 的名称,如果没有配置它,则将方法名称 initUser 作为 Bean 的名称保存到 IoC 容器中。
做好了这些,就可以使用 AnnotationConfigApplicationContext 来构建自己的 IoC 容器了,代码如下:
package com.learn.chapter.main; /**** imports ****/ public class IoCTest { public static void main(String[] args) { // 使用配置文件AppConfig.java创建IoC容器 var ctx = new AnnotationConfigApplicationContext(AppConfig.class); try { // 通过getBean()方法获取Bean var user = ctx.getBean(User.class); System.out.println(user.getUserName()); } finally { // 关闭IoC容器 ctx.close(); } } }
上述代码将 Java 配置文件 AppConfig.java 传递给 AnnotationConfigApplicationContext 的构造方法,这样就能创建 IoC 容器了。IoC 容器会根据 AppConfig 创建 Bean,然后将 Bean 装配进来,这样就可以使用 getBean() 方法获取对应的 Bean 了。
注意,Spring 在默认的情况下,扫描到 Bean 后就创建 Bean 并将 Bean 装配到 IoC 容器中,而不是使用 getBean() 方法后才创建和装配 Bean。运行代码后,可以打印出下面的日志:
......
16:11:04.110 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory –
Creating shared instance of singleton bean 'appConfig'
16:11:04.115 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory –
Creating shared instance of singleton bean 'user'
user_name_1