[Spring] IOC启动顺序

1,022 阅读6分钟

介绍

IOC整个流程大致分为下面几步:

  • IOC容器启动入口是AbstractApplicationContext#在refresh
  • 通过ResourceLoader来解析成资源对象(这里Spring将所有资源都抽象成Resource)
  • 创建默认的Bean注册器DefaultListableBeanFactory
  • BeanDefinitionReader主要是解析和Bean的注册
  • XmlBeanDefinitionReader通过它来解析xml配置中的bean定义,委托BeanDefinition
  • BeanDefinitionRegistry 将BeanDefinition保存到ConcurrentHashMap中

详细说明

那我们就从ContextLoaderListener说起,ContextLoaderListener实现了ServletContextListener接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。在ContextLoaderListener又继承了ContextLoader,所以整个加载配置过程由ContextLoader来完成。

在web容器启动的时候会调用contextInitialized方法,contextInitialized实际调用ContextLoader#initWebApplicationContext方法,那initWebApplicationContext都做了哪些工作:

  • createWebApplicationContext,初始化XmlWebApplicationContext
  • 将ApplicationContext注册到当前servletContext
  • 最后XmlWebApplicationContext#refresh()刷新整个容器

refresh流程图

diagram-1438971934839698549.png

说明

-> prepareRefresh()

--
-> ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
	-> AbstractRefreshableApplicationContext#refreshBeanFactory
		-> createBeanFactory()
			-> 创建 DefaultListableBeanFactory(ListableBeanFactory和BeanDefinitionRegistry接口的默认实现:一个基于bean定义对象的成熟bean工厂)
		-> 配置bean工厂
		-> loadBeanDefinitions
			-> 子类AbstractXmlApplicationContext#loadBeanDefinitions
				-> new XmlBeanDefinitionReader();(创建beanDefault阅读器)
				-> 设置环境
				-> 定义resourceLoader(资源加载器)
				-> 给子类留的入口为了初始化beanDefinition阅读器
				-> loadBeanDefinitions 加载bean
					-> loadBeanDefinitions(XmlBeanDefinitionReader reader)	
						-> AbstractBeanDefinitionReader#loadBeanDefinitions(reader 根据配置文件加载Bean)
							-> getResourceLoader()
							-> XmlBeanDefinitionReader#loadBeanDefinitions(从配置文件读取)
							-> XmlBeanDefinitionReader#doLoadBeanDefinitions
								->doLoadDocument
									->DefaultDocumentLoader#loadDocument
									-> DefaultDocumentLoader#createDocumentBuilderFactory
									-> DocumentBuilder builder=createDocumentBuilder
									-> builder 解析xml文件
								->registerBeanDefinitions
									-> DefaultBeanDefinitionDocumentReader=createBeanDefinitionDocumentReader
									-> 获取已经注册bean的个数
									-> createReaderContext
									-> DefaultBeanDefinitionDocumentReader#
										->doRegisterBeanDefinitions
										-> createDelegate
											-> BeanDefinitionParserDelegate
										-> preProcessXml 处理前 (留给子类实现)
										-> parseBeanDefinitions 
											-> parseDefaultElement(这里面根据Element的name去使用不同的解析)
												-> processBeanDefinition 这里主要解析下这个
													-> BeanDefinitionParserDelegate解析xml为BeanDefinitionHolder
													-> decorateBeanDefinitionIfRequired装饰该bean 依赖的子类的
													-> BeanDefinitionReaderUtils.registerBeanDefinition 向DefaultListableBeanFactory注册beanDefinition
													将beanDefinition保存到DefaultListableBeanFactory#beanDefinitionMap
													-> fireComponentRegistered 发送注册事件
											-> BeanDefinitionParserDelegate#parseCustomElement -- 这里是处理继承NamespaceHandler接口的,实际上就是spring自定义标签解析

										-> postProcessXml 处理后 (留给子类实现)
										
-> prepareBeanFactory()  配置工厂的标准上下文特征 -如上下文的ClassLoader和后处理器
	->beanFactory.setBeanClassLoader
	-> setBeanExpressionResolver 设置表达式编辑器
	-> addPropertyEditorRegistrar 
	为给定的ResourceLoader和PropertyResolver创建一个新的ResourceEditorRegistrar
    -> addBeanPostProcessor bean工厂 配置一些回调类  重点说下这块,因为我们经常实现一些类被spring 回调
    	-> ApplicationContextAwareProcessor,这个Processor的作用在于为实现Aware接口的bean调用该Aware接口定义的方法,并传入对应的参数
    -> ignoreDependencyInterface 设置一些忽略的接口
    -> registerResolvableDependency
    -> addBeanPostProcessor 为什么这里还有一个呢,为什么不放到一起呢
    	-> ApplicationListenerDetector
    -> setTempClassLoader
    -> registerSingleton
    -> addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)
    -> setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()))
    -> registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment())
    -> registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties())
    -> registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment())
->postProcessBeanFactory()
	-> addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig))
	-> ignoreDependencyInterface(ServletContextAware.class)
	-> ignoreDependencyInterface(ServletConfigAware.class)
	-> WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
	-> WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
-> invokeBeanFactoryPostProcessors (实例化并调用所有已注册的BeanFactoryPostProcessor bean,如果给定,则考虑显式的顺序。)
必须在单例实例化之前调用
-> registerBeanPostProcessors 重点
-> initMessageSource()
-> onRefresh()
-> registerListeners()
-> finishBeanFactoryInitialization(beanFactory)
-> finishRefresh()

组件

Resource

  1. Spring把各种类型的文件都可以叫做Resource,只不过对于Spring开发者来说,Resource大多都是xml文件。
  2. Resource 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。

ResourceLoader

字面意思是Resource数据的加载器

PathMatchingResourcePatternResolver

返回资源加载器用于资源位置。可以检查ResourcePatternResolver接口并进行相应的转换,以便为给定的资源模式加载多个资源

BeanFactoryPostProcessor

给使用者留下的扩展点,可以修改,新增BeanDefinition。因为此时所有的BeanDefinition已经加载,但是没有Bean被创建。一般用在需要覆盖或替换Bean的属性时。 该扩展点提供了两个方法:

BeanPostProcessor

是在Bean新创建后,未初始化前调用的。例如在InitializingBean的afterPropertiesSet前,或则自定义的init-method前。 在Bean初始化后,调用方法postProcessAfterInitialization。

ApplicationContext

ApplicationContext 丰富了BeanFactory的功能, 继承了下面几个类

  1. EnvironmentCapable,
  2. ListableBeanFactory,
  3. HierarchicalBeanFactory,
  4. MessageSource,
  5. ApplicationEventPublisher,
  6. ResourcePatternResolver

AbstractApplicationContext 实现了ConfigurableApplicationContext

注:ConfigurableApplicationContext 实现了ApplicationContext, Lifecycle, Closeable AbstractApplicationContext 这个类干的活太多了,核心核心,这里多说一句,spring很多逻辑都放到了抽象类里面,当你看这些代码的时候,找不到调用关系,可以去父类里面看看

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext的构造方法里面干了一些事 这里面调用了AbstractApplicationContext的refresh()方法

BeanDefinitionReader

BeanDefinitionReader不能直接加载配置文件,需要把配置文件封装成Resource, 将Resource转换成BeanDefinition的各种工鞥 然后才能调用重载方法loadBeanDefinitions()

BeanDefinitionReader只是一个标准的bean definition读取器接口,他提供了几个标准方法:

  1. BeanDefinitionRegistry getRegistry();
  2. ResourceLoader getResourceLoader();
  3. ClassLoader getBeanClassLoader();
  4. BeanNameGenerator getBeanNameGenerator();
  5. int loadBeanDefinitions(Resource resource) loadBeanDefinitions有多个重载方法,但是功能都是一样,用于加载bean的定义

首先是getRegistry用于获取一个bean注册的容器类,BeanDefinitionRegistry在这里也是一个接口,具体到类就要提到DefaultListableBeanFactory,

DefaultListableBeanFactory

是整个bean加载的核心部分,是spring注册及加载bean的默认实现 DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory,并且实现了ConfigurableListableBeanFactory、BeanDefinitionRegistry接口

屏幕快照 2018-05-15 下午7.36.55.png

XmlWebApplicationContext

//通过XmlBeanDefinitionReader加载bean定义 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

BeanDefinitionRegistry

作用:用于存放bean定义的注册表的接口,例如RootBeanDefinition和ChildBeanDefinition实例。通常由内部使用AbstractBeanDefinition层次结构的BeanFactories实现. 这是Spring的bean工厂包中唯一封装了bean定义注册的接口。标准的BeanFactory接口仅涵盖对完全配置的工厂实例的访问

DocumentLoader

定义从资源文件加载到转换为Document的功能

BeanDefinitionDocumentReader

实现了BeanDefinitionDocumentReader接口,DefaultBeanDefinitionDocumentReader并不负责任何具体的bean解析,它面向的是xml Document对象,根据其元素的命名空间和名称,起一个类似路由的作用((不过,命名空间的判断,也是委托给delegate来做的),它跟BeanDefinitionParserDelegate协同合作,把解析任务交接BeanDefinitionParserDelegate来做

BeanDefinitionParserDelegate

完成具体Bean的解析(比如、、标签),对于扩展的标签会交给不同的NamespaceHandler跟BeanDefinitionParser来解析

BeanDefinitionParser

解析配置文件成相应的BeanDefinition(context:component-scan,aop:config等标签都是又不同的BeanDefinitionParser来解析),一般在NamespaceHandler中使用。Spring也为自定义BeanDefinitionParser提供了很多支持,在一些抽象类的基础上添加少量功能即可满足大部分需求。

NamespaceHandler

要解析自定义的bean就要通过自己所实现的NamespaceHandler来进行解析。比如定义了http://www.springframework.org/schema/osgi=org.springframework.osgi.config.OsgiNamespaceHandler,那么在碰到osgi的scheme的时候就会去调用OsgiNamespaceHandler来进行解析; 在对于普通的扩展需求来说,只要让自己的Handler继承NamespaceHandlerSupport并实现 init()方法 就好了,对于特殊的扩展需求 则可以自己 来实现NamespaceHandler。

总结

  • Spring的代码结构非常清晰
  • 每个类的职责非常单一,我们写代码也尽量让每个类的只干一件事
  • 我们可以基于BeanPostProcessor做一些框架层面的东西