IOC容器
什么是IOC
Inversion of Control,控制反转,是一种设计思想,意在将对象的创建和管理交给容器,等到需要使用对象的时候,从容器中把对象取出来。打个比方说,你手里有多余的钱,但是你不懂经济学,不会理财,所以将钱存到银行交由银行管理,等到你需要用钱的时候可以随时去取。
IOC设计思想的优点
- 将对象的创建和管理工作交给容器,实现了资源的集中管理
- 降低了资源和使用方的耦合度
- 由于上述原因提高了程序的可扩展性和复用性
什么是DI
Dependency Injection,依赖注入,意思是说在程序的运行过程中动态的向某个对象中注入他需要的其他对象。打个比方说,你要喝水,去超市买了一个杯子,但是杯子是用来喝水的,所以你需要往杯子中注入水,这个杯子才能实现它的价值。
DI是如何实现的
依赖注入是基于反射实现的,反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,当对象创建完毕之后,将其注给目标对象中的属性。
SpringIOC容器ApplicationContext
//创建并初始化一个IOC容器
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
上面的代码可以为我们创建一个完整的IOC容器ApplicationContext
ApplicationContext的继承体系
- BeanFactory:Spring核心接口,称为Spring的Bean工厂
- ApplicationContext:是比BeanFactory更高级接口,称之为 Spring 容器
- FileSystemXmlApplicationContext:Spring容器的实现类,通过加载系统中的文件完成容器的初始化工作
- ClassPathXmlApplicationContext:Spring容器的实现类,通过加载类路径下的Spring.xml配置文件完成容器初始化
- AnnotationConfigApplicationContext:Spring容器的实现类,实现SpringConfig配置类+注解方式的容器初始化
- ApplicationContext:是比BeanFactory更高级接口,称之为 Spring 容器
Spring是如何将对象加载到容器中的以及如何从容器中获取对象
spring为开发人员提供了创建实例的配置方式,将创建对象的工作从以往的在程序中编码的方式转为编写配置文件或注解的方式。不论哪一种方式,核心的流程都是一样的。
//使用@Component声明一个Bean的配置,@Component用来标注一个类需要被加载到Spring容器中
@Component
public class Dog{
}
在spring中,关于对象的配置通常称之为配置一个Bean,可以将Bean理解为一个实例
一些JAVA类的解释:
BeanDefinition:这个类中包含所有Bean的配置信息,容器初始化时,会将Bean的配置信息存储到这个对象,方便后期为其创建实例
beanDefinitionMap:Map类型的集合,这个类中用来存储所有的BeanDefinition
singletonObjects:Map类型的集合,当Spring容器在初始化过程中将beanDefinitionMap中所有的BeanDefinition都循环解析并创建实例后,将这些创建好的对象存储到singletonObjects。
- 容器在初始化过程中会将所有配置的Bean信息分别存储到单独的BeanDefinition对象中,BeanDefinition中包含要创建的这个Bean所有信息。
- 所有的BeanDefinition会被存储到一个名为beanDefinitionMap的Map集合中去。
- Spring框架再对该Map进行遍历并使用反射创建Bean实例对象。
- 将创建好的Bean对象存储在一个名为singletonObjects的Map集合中。
- 在需要使用指定对象时,调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。
代码演示SpringBean实例化流程
下面用一个spring小程序的运行过程来解释SpringBean的实例化流程。创建一个用户类,用户类包含一个宠物类的属性,最终程序会从用户对象中调用宠物对象的eat方法。
示例代码
<!-- spring核心依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.19</version>
</dependency>
</dependencies>
创建一个宠物接口
public interface Pets {
void eat();
}
创建宠物实现类
@Component
public class Dog implements Pets{
@Override
public void eat() {
System.out.println("大黄开始吃东西了");
}
}
创建用户类
@Component
public class User {
@Autowired
private Pets pets;
void petEat(){
pets.eat();
}
}
spring配置类
@Configuration
@ComponentScan("com.demo")
public class SpringConfig {
}
测试类
public class springtest {
public static void main(String[] args) {
//容器初始化
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean(User.class);
user.petEat();
}
}
上述程序执行过程
@Configuration:表示这是一个Spring的配置类
@ComponentScan:指定需要将哪些包下的类加载到容器中
@Component:标注一个类需要被加载到Spring容器中
-
容器初始化时会读取到 @Configuration 注解的配置类 SpringConfig 注册到 BeanDefinition中
-
根据 @ComponentScan(“com.demo”) 扫描com.demo下所有被 @Component 标注的类注册到BeanDefinition中
-
将所有BeanDefinition添加到BeanDefinitionMap中
-
Spring容器在初始化过程中将把BeanDefinitionMap的每个BeanDefinition创建为对象并存放在singletonObjects对象集合中(到这一步,singletonObjects中就包含了SpringConfig、User、Dog对象了)至此,容器初始化基本完成
-
通过ApplicationContext的getBean方法从singletonObjects中获取对象
-
调用吃饭方法,打印结果:『大黄开始吃东西了』