Spring的IoC容器实现以上功能的过程,基本上可以按照类似的流程划分为两个阶段,即容器启动阶段和Bean实例化阶段
启动阶段:
由refresh()方法启动(这个方法在IoC容器的具体实现类的构造函数中),启动包括
1、BeanDefinition的Resource定位(获取资源位置的Resource对象)
构造IoC容器时,需要指定BeanDefinition的信息来源(如从xml文件中来),而这个信息来源需要封装成spring中的Resource类来进行IO操作。Resource是Spring用来封装I/O操作的类。Resource是一个接口,具体实现类如ClassPathResource。通过配置文件指定的文件位置完成BeanDefinition的定位,但具体的数据还没开始读入。
Resource只是BeanDefinition位置的封装,具体操作由ResourceLoader完成。这个过程即找到BeanDefinition的位置,为其载入创造I/O操作的条件。
- 构造函数调用refresh(),该方法在synchronized修饰的同步代码块中,保证不能有多个线程初始化。
- refresh()先调用createBeanFactory()创建容器
- refresh()再调用loadBeanDefinitions(BeanFactory)方法
- loadBeanDefinitions()先调用ResourceLoader得到Resource[]数组(定位)
- loadBeanDefinitions()再调用Reader读取器(如XMLBeanDefinitionReader)读取Resource[]中资源(完成I/O操作,即开始载入),利用回调把结果传入之前建立的BeanFactory中
很多不同的类都有loadBeanDefinitions方法,它们的功能也是不同的。
2、载入(解析xml配置文件,获取各个Element信息)
把用户定义好的Bean表示成IoC容器内部的数据结构(即BeanDefinition)。BeanDefinition的载入分两部分,首先调用XML解析器得到document对象,但这些document对象没有按照spring的bean规则进行解析。接下来按照spring规则进行解析。
- 在Reader读取器(如XMLBeanDefinitionReader的loadBeanDefinitions方法)中调用documentLoader(XML解析器)中的方法,获得document对象;
简单说就是:第一步通过XML解析器把xml文件中的信息解析成document对象(继承了Node本质是节点)。
- 在Reader读取器(如XMLBeanDefinitionReader的loadBeanDefinitions方法)再调用processBeanDefinition(传入document的element);
简单说就是:第二步获取document对象各个element信息(继承了Node本质是节点)。
- processBeanDefinition (element,BeanDefinition)方法调用parseBeanDefinitionElement()方法,解析具体的元素标签(如<bean>、<name>、<id>等),将解析得到的结果设置到BeanDefinitionHolder中(BeanDefinitionHolder是BeanDefinition对象的封装类,封装了BeanDefinition、bean的名字等,用它来完成向IoC容器注册);
简单说就是:第三步解析element中具体的元素标签到BeanDefinition中。
经过对document逐层解析,我们在XML中定义的BeanDefinition就被载入到了IoC容器中。但是,重要的依赖注入在这个时候还没有发生,现在IoC容器中BeanDefinition存在的还只是一些静态的配置信息。
3、容器扩展机制
Spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。
4、注册(将BeanDefinition放入concurrenthashmap中)
将BeanDefinition放入HashMap中,这些BeanDefinition数据在IoC容器中通过一个concurrenthashmap(ConcurrentHashMap<String,BeanDefinition>)来保持和维护,beanName为key,beanDefinition为value。
- 在第上面最后一步processBeanDefinition()方法中调用registerBeanDefinition(BeanDefinitionHolder)开始注册,map.put(beanName,BeanDefinition),registerBeanDefinition中需要synchronized同步代码块,保证数据一致性。以上过程都是在Reader读取器类中完成的。
实例化阶段
1、初始化/实例化被请求对象,并为其添加属性。
2、其他处理
微观上:
1、当外部或直接调用getBean()方法后,通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。并完成依赖注入。
依赖注入的过程是用户第一次向IoC容器索要Bean时触发的(BeanFactory.getBean(name)),当然也有例外,也就是我们可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化(在初始化过程中完成,即在refresh()(初始化的入口)时调用getBean())。通过依赖注入生成实例。
- getBean()方法中,首先判断是否已经创建,且为单例,这种情况不要创建。然后根据Bean的名字获取BeanDefinition,当前取不到就到双亲BeanFactory中取,如果还取不到就顺着双亲链一直向上。
- 取到BeanDefinition后,获取当前Bean所依赖的所有Bean,并递归调用getBean。
- 通过createBean创建Bean的实例,最后返回Bean。
- createBean()中,调用createBeanInstance()创建Bean(默认使用CGLIB一个常用的字节码生成器类库、或使用JVM的反射);
- createBean()中,再调用populateBean()方法,将当前Bean的依赖注入,通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。
2、由BeanWrapper对构造完成的bean实例进行包裹,返回相应的BeanWrapper实例。并对bean实例进行进一步处理,比如设置或者获取bean的相应属性值。
3、检查当前bean实例是否在java代码中实现了一系列的以Aware命名结尾的接口定义,如果是,则该bean实例可以获得相应接口中的方法。
4、检查容器中是否有BeanPostProcessor实现类,如果有,则在其两个分别在init-method执行前和执行后执行的方法中对实例化的bean对象进行处理。Spring的AOP就是使用BeanPostProcessor来为对象生成相应的代理对象
5、在对象实例化过程调用过“BeanPostProcessor的前置处理”之后,会接着检测当前对象是否实现了InitializingBean接口和xmlbean是否有init-method,如果有则会进一步调用对应方法调整对象实例的状态。
6、“BeanPostProcessor的后置处理“之后,检查是否有destroy-method属性,如果是,就会为该实例注册一个用于对象销毁的回调( Callback),以便在这些singleton类型的对象实例销毁之前,异步地执行销毁逻辑。为对象提供了执行自定义销毁逻辑的机会。
销毁逻辑不适用于prototype的对象
7、实例化阶段结束,返回给调用者。
Spring 针对 Bean 管理中创建对象提供注解 (1) @Component (2) @Service (3) @Controller (4) @Repository |
基于注解方式实现属性注入 (1) @Autowired:根据属性类型进行自动装配 |
2) @Qualifier:根据名称进行注入 |
3) @Resource:可以根据类型注入,可以根据名称注入 |
(4) @Value:注入普通类型属性 |