Spring IoC容器设计原理
对Spring IoC容器,总是能听到几个类似的描述:”Spring IoC原理”,”Spring IoC加载过程”等等,这两个描述都对,容器加载的过程,也就是它的原理。关于Spring IoC,先梳理一下容器体系,主线有两条,一条是以BeanFactory继承体系,一条是以ApplicationContext继承体系。
BeanFactory作为基本的容器接口,提供了最基本的功能,从方法名可以看出各个方法的功能
ApplicationContext是高级形态的容器接口,从继承体系看到,ApplicationContext继承了LifeCycle,具备了在容器启动或者关闭时,触发start/stop调用,在AbstractApplicationContext里,会触发各种实现了LifeCycle的start/stop方法的调用。MessageSource跟国际化有关,ApplicationEventPublisher在IoC容器体系中,是一个事件发布器,发布ApplicationEvent事件。
以上的整体体系,大概有个印象即可,接下来,我们从一个简单的测试用例开始,一步步了解IoC容器到底是怎样的,这里还是以最常用的ClassPathXmlApplicationContextTests入手。
容器的加载过程
1、定位Bean的资源文件
2、从资源文件里解析并转为Spring的BeanDefinition,并放入容器
3、初始化Bean,依赖注入
ClassPathXmlApplicationContextTests
1 | java复制代码@Test |
这里创建了ClassPathXmlApplicationContext对象,构造器最终是调用
ClassPathXmlApplicationContext
1 | java复制代码public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException { |
refresh是IoC容器启动的核心方法
AbstractApplicationContext
1 | java复制代码 |
obtainFreshBeanFactory方法
AbstractApplicationContext
1 | java复制代码protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
refreshBeanFactory()和getBeanFactory()都是抽象方法,留给子类实现
AbstractRefreshableApplicationContext
1 | java复制代码protected final void refreshBeanFactory() throws BeansException { |
refreshBeanFactory()首先是判断容器是否存在,存在就销毁,然后重新创建一个DefaultListableBeanFactory,接下来的核心是loadBeanDefinitions(beanFactory)
loadBeanDefinitions()方法
AbstractRefreshableApplicationContext
1 | java复制代码protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { |
从一开始的setConfigLocations,已经定位到Bean的资源文件,而这里,就是要加载并解析Bean定义,解析的过程实质是委托给XmlBeanDefinitionReader
XmlBeanDefinitionReader
1 | java复制代码protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) |
真正把xml解析成BeanDefinition是在registerBeanDefinition方法
XmlBeanDefinitionReader
1 | java复制代码public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { |
在这个方法里,documentReader.registerBeanDefinitions(doc, createReaderContext(resource)) 进行解析,具体的解析工作,是委托给BeanDefinitionParserDelegate类完成的
在这里要注意,XmlBeanDefinitionReader是持有BeanFactory对象的,createReaderContext(resource)方法里,把XmlBeanDefinitionReader的this传递给了XmlReaderContext(createReaderContext的返回对象),也就是说,XmlReaderContext间接持有BeanFactory对象
DefaultBeanDefinitionDocumentReader
1 | java复制代码public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { |
DefaultBeanDefinitionDocumentReader
1 | java复制代码protected void doRegisterBeanDefinitions(Element root) { |
从这个方法可以知道,真正解析的确实是BeanDefinitionParserDelegate,而BeanDefinitionParserDelegate也是间接持有BeanFactory对象。继续进入parseBeanDefinitions(root, this.delegate),会进入到
AbstractBeanDefinitionParser
1 | java复制代码public final BeanDefinition parse(Element element, ParserContext parserContext) { |
这里再说明一下,ParserContext也是间接持有BeanFactory对象,而直接持有的是XmlReaderContext对象。BeanDefinition已经解析出来,并且再次封装为BeanDefinitionHolder,接下来就该是放入到容器里了,逻辑在registerBeanDefinition(holder, parserContext.getRegistry());进去后,会进入到BeanDefinitionReaderUtils
BeanDefinitionReaderUtils
1 | java复制代码public static void registerBeanDefinition( |
再走进registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),这个方法有点长,省去了前面部分
1 | java复制代码public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) |
结果一目了然,this.beanDefinitionMap.put(beanName, beanDefinition); 所以说,Spring IoC容器本质就是个HashMap。解析工作到这,基本ok了,但是放入容器的beanDefinition,解决了依赖的问题了吗?没有。
到这里,我们已经进入到很深的层级了,所以要退回去,退回到AbstractApplicationContext的obtainFreshBeanFactory(),其实这个方法也基本结束了,直接返回beanFactory,所以我们还得回到refresh(),再贴一下代码
prepareBeanFactory方法
AbstractApplicationContext
1 | java复制代码public void refresh() throws BeansException, IllegalStateException { |
prepareBeanFactory方法,先看一下代码
AbstractApplicationContext
1 | java复制代码protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
这里比较感兴趣的是注册的Aware类,Aware类是Spring提供的一种回调机制,比较常用的如ApplicationContextAware,这里实质是放在一个HashSet里。Aware本身是一个没有定义方法的接口,ApplicationContextAware只有一个setApplicationContext方法,ApplicationEventPublisherAware只有一个setApplicationEventPublisher方法,那么这些Aware方法,是在哪里调用呢?
查一下测试demo:org.springframework.context.support.StaticApplicationContextTests#count,另一个类是org.springframework.context.ACATester#setApplicationContext,断点setApplicationContext,启动count
看调用链得知,是在实例化Bean的时候调用的,实例化过程,后面会继续讲,从调用链来看,有意思的是ApplicationContextAwareProcessor类,这是个BeanPostProcessor,是在Bean实例化时做扩展的,很多场景都有用到,BeanPostProcessor提供了两个方法postProcessBeforeInitialization和postProcessAfterInitialization,所以得知,Aware的那些方法,是在BeanPostProcessor触发调用的
1 | java复制代码private void invokeAwareInterfaces(Object bean) { |
postProcessBeanFactory方法是抽象方法,由子类实现,如AbstractRefreshableWebApplicationContext,把servletContext和servletConfig封装成BeanPostProcessor,注册到容器中
invokeBeanFactoryPostProcessors方法
1 | java复制代码protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { |
invokeBeanFactoryPostProcessors主要是执行之前注册的BeanFactoryPostProcessor,BeanFactoryPostProcessor和BeanPostProcessor一样,都是Spring的扩展点,单二者的使用场景不同,BeanFactoryPostProcessor提供了一个方法postProcessBeanFactory,在容器的BeanFactory初始化之后(指BeanDefinition已经加载进容器了,但是还没实例化),可以通过这个方法去对早期暴露的Bean覆盖或者增加Bean的一些配置
1 | java复制代码public interface BeanFactoryPostProcessor { |
一个典型的例子是ConfigurationClassPostProcessor,该接口继承BeanFactoryPostProcessor,用来对Configure配置类做增强
1 | java复制代码/** |
registerBeanPostProcessors方法
registerBeanPostProcessors方法注册BeanPostProcessor,在Bean实例化是调用其方法,该方法委托PostProcessorRegistrationDelegate的registerBeanPostProcessors实例化和注册BeanPostProcessor,
1 | java复制代码public static void registerBeanPostProcessors( |
这个方法的逻辑挺简单,主要是把BeanPostProcessor根据PriorityOrdered和Ordered排序,并添加到BeanFactory里,最后是把没有排序的也添加到BeanFactory
到此,需要说明一下,BeanDefinition已经注册到容器里了,但是还没进行实例化,也没有进行依赖注入,BeanPostProcessor也注册到容器了。
看到这,可以发现AbstractApplicationContext继承体系,就是典型的模板方法的使用案例
本文转载自: 掘金