IOC原理简介
关于 IOC
,如果只介绍理论知识还是比较难理解的,因此,本小节先展示一个 IOC
小案例,读者可通过案例理解在 Spring Boot
中如何使用 IOC
。然后,通过介绍 IOC
容器,说明 IOC
内部的原理。
IOC小案例
为了方便编写代码,先看一下实例结构,如图 4.1 所示。

首先,新建 ioc
包,用于存放 IOC
中的程序(ioc
作为包时,应小写,其他包也是如此)。然后新建 pojo
包,用于存放 Bean
对象。这里新建 Student
类,Student.java
的代码如下所示。
package com.springBoot.ioc.pojo;
/**
* @Desciption 一个简单的Bean对象
*/
public class Student {
//三个基本属性
private Long id;
private String username;
private String password;
//generate setters and getters
//……//
}
然后,再新建配置文件 config
包,用于存放配置文件。在这里新建 MySpringBootConfig
类,代码如下所示。
package com.springBoot.ioc.springBoot.ioc.config;
/**
* @Desciption 配置文件
*/
@Configuration
public class MySpringBootConfig {
@Bean(name="student")
public Student getStu(){
Student student=new Student();
student.setId(1L);
student.setUsername("Tom");
student.setPassword("123456");
return student;
}
}
@Configuration
注解用来说明这是一个配置文件,Spring Boot
会根据这个注解生成 IOC
容器,以便装配 Bean
。@Bean
注解用来装配 Bean
,在注解下方生成的 Bean
会被装配到 IOC
容器中。在上面的代码中,有一个 name
属性,返回 Bean
的 id
是 student
,如果不写 name
属性,会把方法的名称作为 Bean
的 id
放入 IOC
容器中。在编写代码的过程中,建议读者写 name
属性。
最后,进行测试。新建 test
包,用于存放测试类。IocTestDemo
代码如下所示。
package com.springBoot.ioc.test;
/**
* @Desciption IOC实例测试类
*/
public class IocTestDemo {
private static final Logger log=LoggerFactory. getLogger(IocTestDemo.class);
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(MySpringBootConfig.class);
Student student=context.getBean(Student.class);
log.info("id= "+(student.getId()+""));
log.info("username= "+student.getUsername());
log.info("password= "+student.getPassword());
}
}
从上面的代码中可以看到,测试类通过 Annotation Config App lication Context
构建 IOC
容器,因为这样可以读取配置文件 MySpringBootConfig
。有时使用输出的方式效果不够好,所以可以通过日志来检查效果,如下所示。
23:16:25.597[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'student'
23:16:25.597 [main] INFO com.springBoot.ioc.config.IocTestDemo - id= 1
23:16:25.597 [main] INFO com.springBoot.ioc.config.IocTestDemo username= Tom
23:16:25.597 [main] INFO com.springBoot.ioc.config.IocTestDemo password= 123456
这里是最后显示的日志,前面还有很多 Bean
生成的日志,这里先不说明,后面会进一步说明。在此处的日志上,我们可以看到 student
被获取到,然后返回 student
的具体属性内容。
IOC简介
在 IOC
简介中主要介绍 IOC
中的顶级接口 BeanFactory
,然后再介绍 ApplicationContext
接口,最后说明 Spring Boot
中使用的 IOC
实现类 AnnotationConfigApplicationContext
。
BeanFactory接口
在 Spring Boot
中有一个顶级接口 BeanFactory
,而我们知道 IOC
主要用来管理 Bean
,所以所有的 IOC
容器都需要实现这个接口,才具有容器的基本功能。为了后面更容易说明原理,我们先看一下这个接口的基本用法,如下所示。
package org.springframework.beans.factory;
/**
*接口BeanFactory
*/
public interface BeanFactory {
//用&符号获取BeanFactory本身,用来区分通过容器获取FactoryBean产生的对象和FactoryBean本身
String FACTORY_BEAN_PREFIX = "&";
/**
* 使用不同的Bean检索方法,从IOC容器中得到所需要的Bean,从而忽略具体的IOC实现
*/
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//是否包含bean
boolean containsBean(String name);
//指定名字的Bean是否是Singleton类型
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//指定名字的Bean是否是Prototype类型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//查询指定名字的Bean的Class类型是否是特定的Class类型
boolean isTypeMatch(String name, ResolvableType typeToMatch)
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch)
//查询指定名字的Bean的Class类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//查询指定名字的Bean的所有别名
String[] getAliases(String name);
}
在代码中,我们可以看到重点的部分是多个 getBean
方法,使用不同的方法获取 Bean
,例如使用 Bean
名称。Bean
类型也是比较常用的方式。在接口中,还有两个判断 Bean
类型的函数,如果 Bean
是 Singleton
,在获取 Bean
时,就都是同一个 Bean
对象。在默认情况下,Bean
都是 Singleton
,即单例;而 Prototype
类型的 Bean
则相反,每次取 Bean
对象都会创建新的对象。
在代码第 8 行有个常量 FACTORY_BEAN_PREFIX
,用 &
符号获取 BeanFactory
本身,可用来区分通过容器获取 FactoryBean
产生的对象和 FactoryBean
本身。
高级形态的IOC容器ApplicationContext
在上文介绍了顶级接口,但是其功能还不够强大,所以继续介绍新的接口 Application Context
。它是应用上下文接口,不仅可细化 BeanFactory
接口,还可扩展 Application EventPublisher
、MessageSource
、ResourcePatternResolver
等接口,使 IOC
容器的支持更加高级,是高级形态的 IOC
容器。
其中 ApplicationEventPublisher
是应用事件发布接口,ResourcePatternResolver
是资源可配置接口,MessageSource
是消息国际化接口,Application Context
类关系如图 4.2 所示。

基于注解的IOC容器AnnotationConfigApplicationContext
在上文的实例中已经使用过这个类,用来构建 IOC
容器,读取配置文件。在 Spring Boot
中,主要通过注解的方式来装配 Bean
对象,避免加载 application.xml
文件,所以必须要讲解这个类。在讲解之前,先了解它们之间的关系。
建议使用 IDEA
生成这个关系图。首先进入 AnnotationConfigApplicationContext
中,然后单击右键,选择 show Diagrams
。关系图如图 4.3 所示(仅显示需要内容)。

从图 4.3 中可以发现左侧仍是 ApplicationContext
的接口关系图,右侧才是要介绍的。
-
GenericApplicationContext
:在图的右下方,是通用应用上下文类。在这里需要说到父类,就是AbstractApplicationContext
。因为在设计它时,使用了模板方法设计模式,对于某些方法需要在具体的子类中实现。GenericApplicationContext
持有一个单例的固定DefaultListableBeanFactory
实例,在创建GenericApplicationContext
实例时就会创建DefaultListableBeanFactory
实例。在访问Bean
前,DefaultListableBeanFactory
先注册所有的definition
。 -
AnnotationConfigRegistry
:在图 4.3 的右下方,是注解配置注册表。用于注解配置应用上下文的通用接口,拥有一个注册配置类和一个扫描配置类。 -
AbstractApplicationContext
:在GenericApplicationContext
中有一些解释。 -
BeanDefinitionRegistry
:持有×××BeanDefinition
实例的Bean definitions
的注册表接口。DefaultListableBeanFactory
实现了这个接口,所以可以向BeanFactory
里面注册Bean
。又因为GenericApplicationContext
内置一个DefaultListableBeanFactory
实例,所以它对这个接口的实现实际上是通过调用这个实例的相应方法实现。