主要内容
1、IoC和DI的概念
2、IoC容器概述
3、IoC容器的初始化
4、依赖注入过程
5、Bean的生命周期
一、IoC和DI概念
IoC:Inverse of Control(控制反转):将原本在对象中依赖对象创建的控制权,交由Spring IoC容器创建管理。
DI:Dependency Injection(依赖注入):是IoC的另一种描述,指Spring管理对象的依赖关系,将对象依赖通过配置(构造器注入、Setter方法注入)然后再进行注入。
总结:Spring IoC容器的工作,管理对象生命周期和对象间依赖关系。
二、IoC容器概述
a、什么是IoC容器
从BeanFactory源码中了解到,大致就是管理对象的工厂,定义了基本获取对象的方法,和判断对象的某些特性的方法。
public interface BeanFactory {
//对FactoryBean转义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象
String FACTORY_BEAN_PREFIX = "&";
//通过名称获取bena实例
Object getBean(String name) throws BeansException;
//按照类象获得Bean,requiredType可以是父类
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
//按照给定的参数创建bean实例(仅在创建新实例时才应用,而不是在检索现有实例时应用)
Object getBean(String name, Object... args) throws BeansException;
//根据类型获得Bean
<T> T getBean(Class<T> requiredType) throws BeansException;
//按照给定的参数创建对应的Bean(仅在创建新实例时才应用,而不是在检索现有实例时应用)
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//根据name判断,是否含有某个Bean
boolean containsBean(String name);
//是否单例Bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//是否为原型Bean
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//检查bean是否和给定的类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//获取Bean的Calss类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//获取Bean别名
String[] getAliases(String name);
}
b、IoC容器有两个系列容器,接口是:BeanFactory系列和ApplicationContext系列
继承关系如下:
区别:为什么有BeanFactory还需要ApplicationContext,从接口依赖关系可以知道
1、支持不同的信息源:国际化支持。
2、资源访问:Resource rs = ctx. getResource(“classpath:a.properties”)或者“file:c:/a.properties”。
3、容器事件:监听容器的启动、刷新、停止、暂停。
c、常使用的IoC容器实现类
BeanFactory系列:
DefaultListableBeanFactory
XmlBeanFactory
ApplicationContext系列:
XmlWebApplicationContext
AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
三、IoC容器的初始化
a、涉及接口说明
1、Resource接口,可以通用地访问文件资源。
2、ResourceLoader类用于载入Resource
3、BeanDefinition接口,是配置文件< bean >元素标签在容器中地内部表示。< bean >元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应地beanClass、scope、lazyInit类属性与之相互对应。
b、初始化过程(三步骤)
简述:
1、定位:Resource定位。
2、载入:BeanDefinitionReader将Resource定位好的资源载入到BeanDefinition。
3、注册:BeanDefinitionRegistry将BeanDefiniton注册到容器(ConcurrentHashmap)中。
详细说明:
1、定位
Resource是Sping中用于封装I/O操作的接口。在创建spring容器时,通常要访问XML配置文件,除此之外还可以通过访问文件类型、二进制流等方式访问资源,还有当需要网络上的资源时可以通过访问URL,Spring把这些文件统称为Resource,常用的resource资源类型如下:
1、FileSystemResource:以文件的绝对路径方式进行访问资源,效果类似于Java中的File;
2、ClassPathResourcee:以类路径的方式访问资源,效果类似于this.getClass().getResource("/").getPath();
3、ServletContextResource:web应用根目录的方式访问资源,效果类似于request.getServletContext().getRealPath("");
4、UrlResource:访问网络资源的实现类。例如file: http: ftp:等前缀的资源对象;
5、ByteArrayResource: 访问字节数组资源的实现类。
在资源定位过程完成以后,就为资源文件中的bean的载入创造了I/O操作的条件,如何读取资源中的数据将会在下一步介绍的BeanDefinition的载入过程中描述。
2、通过返回的resource对象,进行BeanDefinition的载入
在Spring中配置文件主要格式是XML,以XML为例,对于用来读取XML型资源文件来进行初始化的IoC 容器而言,该类容器会使用到AbstractXmlApplicationContext类,该类定义了一个名为loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法用于获取BeanDefinition
此方法在具体执行过程中首先会new一个与容器对应的BeanDefinitionReader型实例对象,然后将生成的BeanDefintionReader实例作为参数传入loadBeanDefintions(XmlBeanDefinitionReader),继续往下执行载入BeanDefintion的过程。例如AbstractXmlApplicationContext有两个实现类:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext,这些容器在调用此方法时会创建一个XmlBeanDefinitionReader类对象专门用来载入所有的BeanDefinition。
该过程的最终结果就是将XML解析并转化为BeanDefinition。
3、将BeanDefiniton注册到容器
最终Bean配置会被解析成BeanDefinition并与beanName,Alias一同封装到BeanDefinitionHolder类中, 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),注册到DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。
public class DefaultListableBeanFactory private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(64); |
四、依赖注入过程
a、简述:
发生依赖注入的启动为getBean方法,该方法做的就是创建对象和正确的设置好依赖关系,以及初始化(调用一些初始化方法)。
也可以通过lazy-init属性控制Bean的依赖注入时机。当lazy-init=true时,依赖注入会发生在第一次向容器获取Bean时(getBean);当lazy-init=false时,会在容器初始化的过程中将所有的singleton bean提前进行实例化和依赖注入,因此singleton bean的依赖注入是容器初始化过程的一部分,这也是ApplicationContext的默认配置,BeanFactory的默认配置是true。
b、简单流程
1、以DefaultListableBeanFactory为例,其基类AbstractBeanFactory中实现了getBean方法,getBean中调用了doGetBean。
2、doGetBean方法中。在获取Bean时,先从缓存中能取得Bean,如果取得那些已经被创建过的Singleton的Bean,对这种Bean的请求不需要重复创建,直接返回。如果缓存中不能取得的话,并且有parentBeanFactory的话,就从parentBeanFactory中取,如果parentBeanFactory也有父容器,并且在当前父容器没找到就一直往上找,如果某层父容器中有的话,创建操作就在parentBeanFactory。如果parentBeanFactory中也取不到的话,那就根据Bean的类型(Singlton或Prototype或其它)去创建Bean(createBean方法)。
4、createBean这个方法有两个重要点首先创建Bean实例(createBeanInstance),然后进行依赖注入(populateBean),最后初始化Bean及调用的初始化方法(initializeBean)。
5、createBeanInstance方法通过适当的实例化策略来创建一个Bean实例。在这个过程中,通过默认构造方法(无参构造)是基本的实例化策略,这个策略Spring的实现中提供了两种创建Bean的方式:其一,通过JVM反射创建Bean实例;其二,通过cgLib动态代理创建Bean实例(默认)。
6、populateBean方法是对bean实例进行依赖关系设置,完成依赖注入过程。首先由InstantiationAwareBeanPostProcessors进行依赖注入前的处理,判断是否对Bean的Property进行注入,接下来先处理autowire的注入,这里的autowire指的是在配置文件中通过autowire="byName"或者autowire="byType"等属性配置的bean,最后处理Property属性注入。
7、initializeBean方法用于初始化Bean。调用的初始化方法如下:
1、如果Bean实现了Aware系列的接口的话,调用这些接口的实现。
2、对BeanPostProcessor后置处理器的postProcessBeforeInitialization和postProcessAfterInitialization 回调方法的调用,为Bean实例初始化之前和之后做一些处理。
3、调用Bean实例对象初始化的方法,这个初始化方法是在Spring Bean定义配置文件中通过init-method属性指定的。
8、Bean的预实例化。当为Bean配置了lazy-init=false属性时,IOC容器会在初始化的时候对Bean进行预实例化,这时将会提前触发Bean的依赖注入过程,回到AbstractApplicationContext的refresh方法中,这里是IOC容器初始化的入口,在这个方法中调用了finishBeanFactoryInitialization方法,这个方法将会对非延迟加载的单例Bean进行预实例化。在预实例化的过程中调用了getBean方法来触发Bean的依赖注入,后续的处理与前面分析的过程一样,通过一系列的处理,Bean的预实例化过程就完成了。
9、这样就完成了依赖注入过程。该Bean以及Bean之间的依赖关系建立完成以后,通过IoC容器的相关接口方法,就可以提供给上层应用使用了。
五、Bean的生命周期
1、实例化一个Bean,可以理解为new,当然不是new那么简单而是通过反射或者Cglib(默认);
2、按照Spring上下文对实例化的Bean进行配置,也就是IOC注入;
以下是一系列Aware回调接口,用于获取IoC容器的一些信息
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化之前调用的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法或者如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。