Spring IoC详解
Spring IoC 概述
IoC:控制反转理念
- 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
- 正控:若要使用某个对象,需要自己去负责对象的创建。
- 反控:若要使用某个对象,只需要从Spring容器中获取需要使用的对象,不关心对象的创建过程。(把创建对象的控制权反转给了Spring框架)
Spring IoC 阐述
控制反转的概念:控制反转是一种通过描述(在Java中可以是XML或者注解)并通过第三方(Spring)去产生或获取特定对象的方式。
好处:降低对象之间的耦合、我们不需要理解一个类的具体实现,只需要直到它有什么用就好了(直接向IoC容器拿)。
Spring IoC 容器
Spring会提供IoC容器来管理和容纳我们所开发的各种各样的Bean,并且我们可以从中获取各种发布在Spring IoC容器里的Bean,并且通过描述可以得到它。
Spring IoC 容器的设计
Spring IoC 容器的设计主要是基于以下两个接口:
- BeanFactory
- ApplicationContext
其中ApplicationContext是BeanFactory的子接口之一,换句话说:BeanFactory 是 Spring IoC 容器所定义的最底层接口,而ApplicationContext是其最高级接口之一,并对BeanFactory功能做了许多的拓展,所以在绝大部分的工作场景下,都会使用ApplicationContext作为Spring IoC容器。
BeanFactory
从上图可以看到,BeanFactory位于设计的最底层,它提供了Spring IoC最底层的设计,它提供的方法有:
- 【getBean】对应了多个方法来获取配置给Spring IoC容器的Bean。
- 按照类型获取bean:
bean = (Bean) factory.getBean(Bean.class);
要求在Spring中只配置了一个这种类的实例,否则报错。 - 按照bean的名字获取bean:
bean = (Bean) factory.getBean("beanName");
这种方法不太安全,IDE不会检查其安全性(关联性) - 按照名字和类型拿bean:(推荐)
bean = (Bean) factory.getBean("beanName", Bean.class);
- 按照类型获取bean:
- 【isSingleton】用于判断是否单例,如果判断为真,意思是该Bean在容器中是作为一个唯一单例存在的。
- 【isPrototype】与isSingleton相反,如果判断为真,意味着当你从容器中获取Bean,容器就为你生成一个新的实例。
默认情况下,【isSingleton】为true,而【isPrototype】为false。
- 【isTypeMatch】一个按Java类型匹配的方式。
- 【getAliases】获取别名的方法。
这就是Spring IoC最底层的设计,所有关于Spring IoC的容器将会遵守它所定义的方法。
ApplicationContext
根据ApplicationContext的类继承关系图,可以看到ApplicationContext接口拓展了许许多多的接口,因此它的功能十分强大,在实际应用中很常见。
ClassPathXmlApplicationContext:
是ApplicationContext的一个子类,应用:
- 现在src目录下创建一个bean.xml文件
<?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">
<!-- 通过 xml 方式装配 bean -->
<bean name="source" class="pojo.Source">
<property name="fruit" value="橙子"/>
<property name="sugar" value="多糖"/>
<property name="size" value="超大杯"/>
</bean>
</beans>
这里定义了一个bean,这样Spring IoC容器在初始化的时候就能找到它们,然后使用ClassPathXmlApplicationContext容器就可以将其初始化:(Source是一个javaBean,有三个属性:fruit、sugar、size及其get和set方法)
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Source source = (Source) context.getBean("source", Source.class);
System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());
这样就会使用Application的实现类ClassPathXmlApplicationContext去初始化Spring IoC容器,然后开发者就可以通过IoC容器来获取资源了。
AppilicationContext 常见实现类
- ClassPathXmlApplicationContext:
读取classpath中的资源:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- FileSystemXmlApplicationContext:
读取指定路径的资源:
ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
- XmlWebApplicationContext:
需要在web的环境下才可以运行:
XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 这时并没有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext对象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路径,开头的斜线表示Web应用的根目录
ac.refresh(); // 初始化容器
BeanFactory 和 ApplicationContext 的区别
- BeanFactory: 是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置、创建和管理bean。在应用中,一般不使用BeanFactory,而推荐使用ApplicationContext(应用上下文)。
- ApplicationContext: 继承了BeanFactory,拥有基本的IoC功能,此外,还提供以下功能:
- 支持国际化
- 支持消息机制
- 支持统一的资源加载
- 支持AOP功能
Spring IoC 容器的初始化和依赖注入
Spring IoC 容器的两大步骤: Bean的定义、Bean的初始化和依赖注入。
- Bean 的定义分为3步:
- Resource 定位
Spring IoC 容器先根据开发者的配置,进行资源的定位(通过XML或注解) - BeanDefinition 的载入
将Resource 定位到的信息,保存到Bean定义(BeanDefinition)中,此时并不会创建Bean的实例 - BeanDefinition 的注册
这个过程就是讲BeanDefinition 的信息发布到Spring IoC容器中
注意:此时仍然没有对应的Bean的实例。
完成了以上3步,Bean就在Spring IoC容器中被定义了,但还没有被初始化,更没有完成依赖注入。
- Resource 定位
- 初始化和依赖注入
Spring Bean 还有一个配置选项——【lazy-init】,是否延迟初始化Spring Bean。在没有任何配置的情况下,它的默认值为default,实际值为false,即Spring IoC默认会自动初始化Bean。如果将其设置为true,那么只有当我们使用Spring IoC容器的getBean方法获取它的时候,它才会进行Bean的初始化,完成依赖注入。
IoC 是如何实现的
想象如果我们自己来实现这个依赖注入的功能,我们怎么做?
- 读取标注或者配置文件,看看依赖的是哪个Java类(JavaBean),拿到类名。
- 使用反射的API,基于类名实例化对应的对象实例。
- 将对象实例化,通过构造函数或者setter,传递给引用。